From 599c92e4aed3d44bf1d246968db909e06a48fa10 Mon Sep 17 00:00:00 2001 From: Marcel Gaupp Date: Thu, 9 Jan 2025 19:27:17 +0100 Subject: [PATCH 01/16] Programming exercises: Fix programming language feature flags (#10110) --- ...kinsProgrammingLanguageFeatureService.java | 4 +-- ...alCIProgrammingLanguageFeatureService.java | 34 +++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java index 6ad7b4c65aec..a858a171efd7 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java @@ -44,9 +44,9 @@ public JenkinsProgrammingLanguageFeatureService() { programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, true, false, List.of(), false)); programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, false, false, false, false, true, List.of(), false)); programmingLanguageFeatures.put(JAVA, - new ProgrammingLanguageFeature(JAVA, true, true, true, true, false, List.of(PLAIN_GRADLE, GRADLE_GRADLE, PLAIN_MAVEN, MAVEN_MAVEN, MAVEN_BLACKBOX), true)); + new ProgrammingLanguageFeature(JAVA, true, true, true, true, false, List.of(PLAIN_GRADLE, GRADLE_GRADLE, PLAIN_MAVEN, MAVEN_MAVEN, MAVEN_BLACKBOX), false)); programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), false)); - programmingLanguageFeatures.put(KOTLIN, new ProgrammingLanguageFeature(KOTLIN, true, false, true, true, false, List.of(), true)); + programmingLanguageFeatures.put(KOTLIN, new ProgrammingLanguageFeature(KOTLIN, true, false, true, true, false, List.of(), false)); programmingLanguageFeatures.put(PYTHON, new ProgrammingLanguageFeature(PYTHON, false, false, true, false, false, List.of(), false)); programmingLanguageFeatures.put(R, new ProgrammingLanguageFeature(R, false, false, true, false, false, List.of(), false)); programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false)); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java index b8478cf65daf..0c46a202574b 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java @@ -43,23 +43,23 @@ public class LocalCIProgrammingLanguageFeatureService extends ProgrammingLanguag public LocalCIProgrammingLanguageFeatureService() { // Must be extended once a new programming language is added - programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), false)); - programmingLanguageFeatures.put(ASSEMBLER, new ProgrammingLanguageFeature(ASSEMBLER, false, false, false, false, false, List.of(), false)); - programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, true, true, false, false, List.of(FACT, GCC), false)); - programmingLanguageFeatures.put(C_PLUS_PLUS, new ProgrammingLanguageFeature(C_PLUS_PLUS, false, false, true, false, false, List.of(), false)); - programmingLanguageFeatures.put(C_SHARP, new ProgrammingLanguageFeature(C_SHARP, false, false, true, false, false, List.of(), false)); - programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, true, false, List.of(), false)); - programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, true, false, false, false, true, List.of(), false)); + programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), true)); + programmingLanguageFeatures.put(ASSEMBLER, new ProgrammingLanguageFeature(ASSEMBLER, false, false, false, false, false, List.of(), true)); + programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, true, true, false, false, List.of(FACT, GCC), true)); + programmingLanguageFeatures.put(C_PLUS_PLUS, new ProgrammingLanguageFeature(C_PLUS_PLUS, false, false, true, false, false, List.of(), true)); + programmingLanguageFeatures.put(C_SHARP, new ProgrammingLanguageFeature(C_SHARP, false, false, true, false, false, List.of(), true)); + programmingLanguageFeatures.put(GO, new ProgrammingLanguageFeature(GO, false, false, true, true, false, List.of(), true)); + programmingLanguageFeatures.put(HASKELL, new ProgrammingLanguageFeature(HASKELL, true, false, false, false, true, List.of(), true)); programmingLanguageFeatures.put(JAVA, - new ProgrammingLanguageFeature(JAVA, true, true, true, true, false, List.of(PLAIN_GRADLE, GRADLE_GRADLE, PLAIN_MAVEN, MAVEN_MAVEN), false)); - programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), false)); - programmingLanguageFeatures.put(KOTLIN, new ProgrammingLanguageFeature(KOTLIN, false, false, true, true, false, List.of(), false)); - programmingLanguageFeatures.put(OCAML, new ProgrammingLanguageFeature(OCAML, false, false, false, false, true, List.of(), false)); - programmingLanguageFeatures.put(PYTHON, new ProgrammingLanguageFeature(PYTHON, false, false, true, false, false, List.of(), false)); - programmingLanguageFeatures.put(R, new ProgrammingLanguageFeature(R, false, false, true, false, false, List.of(), false)); - programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), false)); - programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, false, true, true, false, List.of(PLAIN), false)); - programmingLanguageFeatures.put(TYPESCRIPT, new ProgrammingLanguageFeature(TYPESCRIPT, false, false, true, false, false, List.of(), false)); - programmingLanguageFeatures.put(VHDL, new ProgrammingLanguageFeature(VHDL, false, false, false, false, false, List.of(), false)); + new ProgrammingLanguageFeature(JAVA, true, true, true, true, false, List.of(PLAIN_GRADLE, GRADLE_GRADLE, PLAIN_MAVEN, MAVEN_MAVEN), true)); + programmingLanguageFeatures.put(JAVASCRIPT, new ProgrammingLanguageFeature(JAVASCRIPT, false, false, true, false, false, List.of(), true)); + programmingLanguageFeatures.put(KOTLIN, new ProgrammingLanguageFeature(KOTLIN, false, false, true, true, false, List.of(), true)); + programmingLanguageFeatures.put(OCAML, new ProgrammingLanguageFeature(OCAML, false, false, false, false, true, List.of(), true)); + programmingLanguageFeatures.put(PYTHON, new ProgrammingLanguageFeature(PYTHON, false, true, true, false, false, List.of(), true)); + programmingLanguageFeatures.put(R, new ProgrammingLanguageFeature(R, false, false, true, false, false, List.of(), true)); + programmingLanguageFeatures.put(RUST, new ProgrammingLanguageFeature(RUST, false, false, true, false, false, List.of(), true)); + programmingLanguageFeatures.put(SWIFT, new ProgrammingLanguageFeature(SWIFT, false, false, true, true, false, List.of(PLAIN), true)); + programmingLanguageFeatures.put(TYPESCRIPT, new ProgrammingLanguageFeature(TYPESCRIPT, false, false, true, false, false, List.of(), true)); + programmingLanguageFeatures.put(VHDL, new ProgrammingLanguageFeature(VHDL, false, false, false, false, false, List.of(), true)); } } From e0430313848c96168abd93a56c3bdcd8f49818bc Mon Sep 17 00:00:00 2001 From: Paul Rangger <48455539+PaRangger@users.noreply.github.com> Date: Fri, 10 Jan 2025 17:55:36 +0100 Subject: [PATCH 02/16] Communication: Resolve an issue when displaying replies to a post (#10122) --- .../course-conversations.component.html | 2 +- .../course-conversations.component.ts | 4 ++++ .../posting-content-part.components.ts | 8 ++++++-- .../metis/posting-content/posting-content.component.html | 4 ++-- .../metis/posting-content/posting-content.components.ts | 7 +++++++ 5 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html index 691e877f45b7..9e5949f415f4 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.html +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.html @@ -54,7 +54,7 @@ (); @Output() channelReferenceClicked = new EventEmitter(); @@ -60,6 +60,10 @@ export class PostingContentPartComponent implements OnInit { this.processContent(); } + ngOnChanges(): void { + this.processContent(); + } + /** * Opens an attachment with the given URL in a new window * diff --git a/src/main/webapp/app/shared/metis/posting-content/posting-content.component.html b/src/main/webapp/app/shared/metis/posting-content/posting-content.component.html index ecf25fe3a3bb..69edb3cde190 100644 --- a/src/main/webapp/app/shared/metis/posting-content/posting-content.component.html +++ b/src/main/webapp/app/shared/metis/posting-content/posting-content.component.html @@ -16,7 +16,7 @@ @if (showContent) {
- @for (postingContentPart of postingContentParts(); track $index) { + @for (postingContentPart of postingContentParts(); track contentPartTrack($index)) { @if (!previewMode) {
- @for (postingContentPart of postingContentParts(); track $index) { + @for (postingContentPart of postingContentParts(); track contentPartTrack($index)) { Date: Sun, 12 Jan 2025 11:29:13 +0100 Subject: [PATCH 03/16] Development: Add client test to check reactivity of post content (#10126) --- .../posting-content-part.components.ts | 2 +- .../posting-content-part.component.spec.ts | 40 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts b/src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts index b01910f4b81a..e194a5f162eb 100644 --- a/src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts +++ b/src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts @@ -60,7 +60,7 @@ export class PostingContentPartComponent implements OnInit, OnChanges { this.processContent(); } - ngOnChanges(): void { + ngOnChanges() { this.processContent(); } diff --git a/src/test/javascript/spec/component/shared/metis/posting-content/posting-content-part.component.spec.ts b/src/test/javascript/spec/component/shared/metis/posting-content/posting-content-part.component.spec.ts index 5a64278b8145..6020aee32a3b 100644 --- a/src/test/javascript/spec/component/shared/metis/posting-content/posting-content-part.component.spec.ts +++ b/src/test/javascript/spec/component/shared/metis/posting-content/posting-content-part.component.spec.ts @@ -316,4 +316,44 @@ describe('PostingContentPartComponent', () => { expect(escapedContent).toBe('1\\. Numbered item\n\\- Unordered item\n2\\. Another numbered item\n\\- Another unordered item'); }); }); + + describe('PostingContentPart Reactivity', () => { + it('should update display when postingContentPart changes', () => { + component.postingContentPart = { + contentBeforeReference: 'Initial content', + linkToReference: undefined, + queryParams: undefined, + referenceStr: undefined, + contentAfterReference: undefined, + } as PostingContentPart; + fixture.detectChanges(); + + const initialMarkdownElements = getElements(debugElement, '.markdown-preview'); + expect(initialMarkdownElements).toHaveLength(1); + expect(initialMarkdownElements![0].innerHTML).toBe('

Initial content

'); + + fixture.componentRef.setInput('postingContentPart', { + contentBeforeReference: 'Updated content before', + linkToReference: ['/course/1'], + queryParams: { searchText: '#123' }, + referenceStr: '#123', + referenceType: ReferenceType.POST, + contentAfterReference: 'Updated content after', + } as PostingContentPart); + fixture.detectChanges(); + + const updatedMarkdownElements = getElements(debugElement, '.markdown-preview'); + expect(updatedMarkdownElements).toHaveLength(2); + expect(updatedMarkdownElements![0].innerHTML).toBe('

Updated content before

'); + expect(updatedMarkdownElements![1].innerHTML).toBe('

Updated content after

'); + + const referenceLink = getElement(debugElement, '.reference'); + expect(referenceLink).not.toBeNull(); + expect(referenceLink.innerHTML).toInclude('#123'); + + const icon = getElement(debugElement, 'fa-icon'); + expect(icon).not.toBeNull(); + expect(icon.innerHTML).toInclude('fa fa-message'); + }); + }); }); From e62cefe6d19751fb8ad1befe544d39a4c4314f62 Mon Sep 17 00:00:00 2001 From: Marcel Gaupp Date: Sun, 12 Jan 2025 11:33:38 +0100 Subject: [PATCH 04/16] Programming exercises: Add Bash programming exercise template (#10089) --- .../programming-exercise-features.inc | 4 ++ .../domain/ProgrammingLanguage.java | 1 + .../service/TemplateUpgradePolicyService.java | 5 +- .../ci/ContinuousIntegrationService.java | 8 +-- ...kinsProgrammingLanguageFeatureService.java | 2 + .../build_plan/JenkinsBuildPlanService.java | 4 +- ...alCIProgrammingLanguageFeatureService.java | 2 + src/main/resources/config/application.yml | 2 + .../templates/aeolus/bash/default.sh | 33 +++++++++ .../templates/aeolus/bash/default.yaml | 15 ++++ .../templates/bash/exercise/script.bash | 14 ++++ src/main/resources/templates/bash/readme | 26 +++++++ .../templates/bash/solution/script.bash | 13 ++++ .../resources/templates/bash/test/test.bats | 72 +++++++++++++++++++ .../templates/bash/test/test_data/numbers.txt | 3 + .../bash/test/test_data/numbers_expected.txt | 3 + .../bash/test/test_data/rename_me.txt | 1 + .../bash/test/test_helper/common-setup.bash | 34 +++++++++ .../jenkins/bash/regularRuns/pipeline.groovy | 55 ++++++++++++++ .../programming/programming-exercise.model.ts | 1 + src/test/resources/config/application.yml | 2 + 21 files changed, 292 insertions(+), 8 deletions(-) create mode 100644 src/main/resources/templates/aeolus/bash/default.sh create mode 100644 src/main/resources/templates/aeolus/bash/default.yaml create mode 100644 src/main/resources/templates/bash/exercise/script.bash create mode 100644 src/main/resources/templates/bash/readme create mode 100644 src/main/resources/templates/bash/solution/script.bash create mode 100644 src/main/resources/templates/bash/test/test.bats create mode 100644 src/main/resources/templates/bash/test/test_data/numbers.txt create mode 100644 src/main/resources/templates/bash/test/test_data/numbers_expected.txt create mode 100644 src/main/resources/templates/bash/test/test_data/rename_me.txt create mode 100644 src/main/resources/templates/bash/test/test_helper/common-setup.bash create mode 100644 src/main/resources/templates/jenkins/bash/regularRuns/pipeline.groovy diff --git a/docs/user/exercises/programming-exercise-features.inc b/docs/user/exercises/programming-exercise-features.inc index 1b43ab3cae7b..2c29ff79a1f4 100644 --- a/docs/user/exercises/programming-exercise-features.inc +++ b/docs/user/exercises/programming-exercise-features.inc @@ -47,6 +47,8 @@ Instructors can still use those templates to generate programming exercises and +----------------------+----------+---------+ | Go | yes | yes | +----------------------+----------+---------+ + | Bash | yes | yes | + +----------------------+----------+---------+ - Not all ``templates`` support the same feature set and supported features can also change depending on the continuous integration system setup. Depending on the feature set, some options might not be available during the creation of the programming exercise. @@ -91,6 +93,8 @@ Instructors can still use those templates to generate programming exercises and +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ | Go | no | no | yes | yes | n/a | no | L: yes, J: no | +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ + | Bash | no | no | no | no | n/a | no | L: yes, J: no | + +----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+------------------------+ - *Sequential Test Runs*: ``Artemis`` can generate a build plan which first executes structural and then behavioral tests. This feature can help students to better concentrate on the immediate challenge at hand. - *Static Code Analysis*: ``Artemis`` can generate a build plan which additionally executes static code analysis tools. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java index fa3aa6d65577..0c4894182e1e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/domain/ProgrammingLanguage.java @@ -39,6 +39,7 @@ public enum ProgrammingLanguage { private static final Set ENABLED_LANGUAGES = Set.of( ASSEMBLER, + BASH, C, C_PLUS_PLUS, C_SHARP, diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java index a60e8be4b050..9dd3249bd1a4 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/TemplateUpgradePolicyService.java @@ -32,8 +32,9 @@ public TemplateUpgradePolicyService(JavaTemplateUpgradeService javaRepositoryUpg public TemplateUpgradeService getUpgradeService(ProgrammingLanguage programmingLanguage) { return switch (programmingLanguage) { case JAVA -> javaRepositoryUpgradeService; - case KOTLIN, PYTHON, C, HASKELL, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT, C_SHARP, GO -> defaultRepositoryUpgradeService; - case SQL, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + programmingLanguage); + case KOTLIN, PYTHON, C, HASKELL, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT, C_SHARP, GO, BASH -> + defaultRepositoryUpgradeService; + case SQL, MATLAB, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + programmingLanguage); }; } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java index 9fb2ac0b6f56..3f14c1b2a015 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationService.java @@ -219,8 +219,8 @@ enum RepositoryCheckoutPath implements CustomizableCheckoutPath { @Override public String forProgrammingLanguage(ProgrammingLanguage language) { return switch (language) { - case JAVA, PYTHON, C, HASKELL, KOTLIN, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT, C_SHARP, GO -> "assignment"; - case SQL, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + language); + case JAVA, PYTHON, C, HASKELL, KOTLIN, VHDL, ASSEMBLER, SWIFT, OCAML, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT, C_SHARP, GO, BASH -> "assignment"; + case SQL, MATLAB, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + language); }; } }, @@ -230,8 +230,8 @@ public String forProgrammingLanguage(ProgrammingLanguage language) { public String forProgrammingLanguage(ProgrammingLanguage language) { return switch (language) { case JAVA, PYTHON, HASKELL, KOTLIN, SWIFT, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT -> ""; - case C, VHDL, ASSEMBLER, OCAML, C_SHARP, GO -> "tests"; - case SQL, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + language); + case C, VHDL, ASSEMBLER, OCAML, C_SHARP, GO, BASH -> "tests"; + case SQL, MATLAB, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException("Unsupported programming language: " + language); }; } }, diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java index a858a171efd7..41b1254cbb7e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/JenkinsProgrammingLanguageFeatureService.java @@ -1,5 +1,6 @@ package de.tum.cit.aet.artemis.programming.service.jenkins; +import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.BASH; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.C; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.C_PLUS_PLUS; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.C_SHARP; @@ -38,6 +39,7 @@ public class JenkinsProgrammingLanguageFeatureService extends ProgrammingLanguag public JenkinsProgrammingLanguageFeatureService() { // Must be extended once a new programming language is added programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), false)); + programmingLanguageFeatures.put(BASH, new ProgrammingLanguageFeature(BASH, false, false, false, false, false, List.of(), false)); programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, false, true, false, false, List.of(FACT, GCC), false)); programmingLanguageFeatures.put(C_PLUS_PLUS, new ProgrammingLanguageFeature(C_PLUS_PLUS, false, false, true, false, false, List.of(), false)); programmingLanguageFeatures.put(C_SHARP, new ProgrammingLanguageFeature(C_SHARP, false, false, true, false, false, List.of(), false)); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java index c54b617d5546..4975c4ce9a17 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/jenkins/build_plan/JenkinsBuildPlanService.java @@ -185,8 +185,8 @@ private JenkinsXmlConfigBuilder builderFor(ProgrammingLanguage programmingLangua throw new UnsupportedOperationException("Xcode templates are not available for Jenkins."); } return switch (programmingLanguage) { - case JAVA, KOTLIN, PYTHON, C, HASKELL, SWIFT, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT, C_SHARP, GO -> jenkinsBuildPlanCreator; - case VHDL, ASSEMBLER, OCAML, SQL, MATLAB, BASH, RUBY, POWERSHELL, ADA, DART, PHP -> + case JAVA, KOTLIN, PYTHON, C, HASKELL, SWIFT, EMPTY, RUST, JAVASCRIPT, R, C_PLUS_PLUS, TYPESCRIPT, C_SHARP, GO, BASH -> jenkinsBuildPlanCreator; + case VHDL, ASSEMBLER, OCAML, SQL, MATLAB, RUBY, POWERSHELL, ADA, DART, PHP -> throw new UnsupportedOperationException(programmingLanguage + " templates are not available for Jenkins."); }; } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java index 0c46a202574b..e9c7e13b8659 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localci/LocalCIProgrammingLanguageFeatureService.java @@ -2,6 +2,7 @@ import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_LOCALCI; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.ASSEMBLER; +import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.BASH; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.C; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.C_PLUS_PLUS; import static de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage.C_SHARP; @@ -45,6 +46,7 @@ public LocalCIProgrammingLanguageFeatureService() { // Must be extended once a new programming language is added programmingLanguageFeatures.put(EMPTY, new ProgrammingLanguageFeature(EMPTY, false, false, false, false, false, List.of(), true)); programmingLanguageFeatures.put(ASSEMBLER, new ProgrammingLanguageFeature(ASSEMBLER, false, false, false, false, false, List.of(), true)); + programmingLanguageFeatures.put(BASH, new ProgrammingLanguageFeature(BASH, false, false, false, false, false, List.of(), true)); programmingLanguageFeatures.put(C, new ProgrammingLanguageFeature(C, false, true, true, false, false, List.of(FACT, GCC), true)); programmingLanguageFeatures.put(C_PLUS_PLUS, new ProgrammingLanguageFeature(C_PLUS_PLUS, false, false, true, false, false, List.of(), true)); programmingLanguageFeatures.put(C_SHARP, new ProgrammingLanguageFeature(C_SHARP, false, false, true, false, false, List.of(), true)); diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml index 82611f9ceac2..b4984ea6cf97 100644 --- a/src/main/resources/config/application.yml +++ b/src/main/resources/config/application.yml @@ -95,6 +95,8 @@ artemis: default: "ghcr.io/ls1intum/artemis-javascript-docker:v1.0.0" r: default: "ghcr.io/ls1intum/artemis-r-docker:v1.0.0" + bash: + default: "ghcr.io/ls1intum/artemis-bash-docker:v1.0.0" c_plus_plus: default: "ghcr.io/ls1intum/artemis-cpp-docker:v1.0.0" c_sharp: diff --git a/src/main/resources/templates/aeolus/bash/default.sh b/src/main/resources/templates/aeolus/bash/default.sh new file mode 100644 index 000000000000..252faca6aaad --- /dev/null +++ b/src/main/resources/templates/aeolus/bash/default.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -e +export AEOLUS_INITIAL_DIRECTORY=${PWD} +set_permissions () { + echo '⚙️ executing set_permissions' + find "${studentParentWorkingDirectoryName}" -type f -exec chmod +x "{}" + +} + +create_results_directory () { + echo '⚙️ executing create_results_directory' + mkdir results +} + +test () { + echo '⚙️ executing test' + bats --report-formatter junit --output results "${testWorkingDirectory}" || true +} + +main () { + if [[ "${1}" == "aeolus_sourcing" ]]; then + return 0 # just source to use the methods in the subshell, no execution + fi + local _script_name + _script_name=${BASH_SOURCE[0]:-$0} + cd "${AEOLUS_INITIAL_DIRECTORY}" + bash -c "source ${_script_name} aeolus_sourcing; set_permissions" + cd "${AEOLUS_INITIAL_DIRECTORY}" + bash -c "source ${_script_name} aeolus_sourcing; create_results_directory" + cd "${AEOLUS_INITIAL_DIRECTORY}" + bash -c "source ${_script_name} aeolus_sourcing; test" +} + +main "${@}" diff --git a/src/main/resources/templates/aeolus/bash/default.yaml b/src/main/resources/templates/aeolus/bash/default.yaml new file mode 100644 index 000000000000..8852c4ac60e9 --- /dev/null +++ b/src/main/resources/templates/aeolus/bash/default.yaml @@ -0,0 +1,15 @@ +api: v0.0.1 +metadata: + name: "Bash" + id: bash +actions: + - name: set_permissions + script: 'find "${studentParentWorkingDirectoryName}" -type f -exec chmod +x "{}" +' + - name: create_results_directory + script: 'mkdir results' + - name: test + script: 'bats --report-formatter junit --output results "${testWorkingDirectory}" || true' + results: + - name: Bats Test Results + path: "results/*.xml" + type: junit diff --git a/src/main/resources/templates/bash/exercise/script.bash b/src/main/resources/templates/bash/exercise/script.bash new file mode 100644 index 000000000000..1c4b4ceea780 --- /dev/null +++ b/src/main/resources/templates/bash/exercise/script.bash @@ -0,0 +1,14 @@ +# TODO: add shebang + +# TODO: list directory entries + +# TODO: create create_me.txt + +# TODO: delete delete_me.txt + +# TODO: rename rename_me.txt to renamed.txt + +# TODO: replace 2.718 with 3.1415 in numbers.txt + +# TODO: exit with an successful status code +exit 1 diff --git a/src/main/resources/templates/bash/readme b/src/main/resources/templates/bash/readme new file mode 100644 index 000000000000..39a96890d707 --- /dev/null +++ b/src/main/resources/templates/bash/readme @@ -0,0 +1,26 @@ +# Bash Scripting Exercise + +This exercise is designed to help you practice basic Bash scripting skills. +Complete the following tasks by writing a Bash script that performs each action. + +1. [task][Add a Shebang](shebang,shebang_custom_message) +Insert a "shebang" at the top of your script to specify that it should be executed with Bash. +Assume Bash is installed at a standard path. + +2. [task][List Directory Entries](list_dir) +Write a command to list all entries in the current directory, including hidden files, and print the output to the terminal. + +3. [task][Create File](file_creation) +Create a file named `create_me.txt` in the current directory. + +4. [task][Delete a File](file_deletion) +Delete the file named `delete_me.txt` from the current directory. + +5. [task][Rename a File](rename) +Rename the file `rename_me.txt` to `renamed.txt`. + +6. [task][Find and Replace Text](replace) +Replace all occurrences of the number `2.718` with `3.1415` in the file `numbers.txt`. Ensure the changes are saved. + +7. [task][Exit with Success](status_code) +Exit the script with a successful status code. diff --git a/src/main/resources/templates/bash/solution/script.bash b/src/main/resources/templates/bash/solution/script.bash new file mode 100644 index 000000000000..aaa638f3dd4f --- /dev/null +++ b/src/main/resources/templates/bash/solution/script.bash @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +ls -a + +touch create_me.txt + +rm delete_me.txt + +mv rename_me.txt renamed.txt + +sed -i 's/2\.718/3.1415/g' numbers.txt + +exit 0 diff --git a/src/main/resources/templates/bash/test/test.bats b/src/main/resources/templates/bash/test/test.bats new file mode 100644 index 000000000000..272af881969f --- /dev/null +++ b/src/main/resources/templates/bash/test/test.bats @@ -0,0 +1,72 @@ +setup_file() { + BATS_TEST_TIMEOUT=10 +} + +setup() { + load "test_helper/common-setup" + _common_setup + + TEST_DATA="$BATS_TEST_DIRNAME/test_data" + + cp "$TEST_DATA"/{numbers.txt,rename_me.txt} "$BATS_TEST_TMPDIR" + + cd "$BATS_TEST_TMPDIR" + touch delete_me.txt + touch .hidden +} + +@test "shebang" { + first_line=$(head -n 1 "$ASSIGNMENT_ROOT/script.bash") + assert_regex "$first_line" '^#!(/usr)?/bin/(env )?bash$' +} + +@test "shebang_custom_message" { + first_line=$(head -n 1 "$ASSIGNMENT_ROOT/script.bash") + + if ! assert_regex "$first_line" '^#!(/usr)?/bin/(env )?bash$' 2>/dev/null; then + echo "$first_line" \ + | batslib_decorate "first line is not a valid shebang" \ + | fail + fi +} + +@test "list_dir" { + run script.bash + + assert_output --partial delete_me.txt + assert_output --partial numbers.txt + assert_output --partial rename_me.txt + assert_output --partial .hidden +} + +@test "file_creation" { + run script.bash + + assert_file_exists create_me.txt +} + +@test "file_deletion" { + run script.bash + + assert_file_not_exists delete_me.txt +} + +@test "rename" { + run script.bash + + assert_file_not_exists rename_me.txt + assert_file_exists renamed.txt + _assert_file_contents renamed.txt "$TEST_DATA/rename_me.txt" +} + +@test "replace" { + run script.bash + + _assert_file_contents numbers.txt "$TEST_DATA/numbers_expected.txt" +} + +@test "status_code" { + run script.bash + + assert_success +} diff --git a/src/main/resources/templates/bash/test/test_data/numbers.txt b/src/main/resources/templates/bash/test/test_data/numbers.txt new file mode 100644 index 000000000000..a252bd33309e --- /dev/null +++ b/src/main/resources/templates/bash/test/test_data/numbers.txt @@ -0,0 +1,3 @@ +2.718 +2.718 2.718 2.718 +21718 diff --git a/src/main/resources/templates/bash/test/test_data/numbers_expected.txt b/src/main/resources/templates/bash/test/test_data/numbers_expected.txt new file mode 100644 index 000000000000..44121ba5f026 --- /dev/null +++ b/src/main/resources/templates/bash/test/test_data/numbers_expected.txt @@ -0,0 +1,3 @@ +3.1415 +3.1415 3.1415 3.1415 +21718 diff --git a/src/main/resources/templates/bash/test/test_data/rename_me.txt b/src/main/resources/templates/bash/test/test_data/rename_me.txt new file mode 100644 index 000000000000..0bc73a7a2f76 --- /dev/null +++ b/src/main/resources/templates/bash/test/test_data/rename_me.txt @@ -0,0 +1 @@ +example content diff --git a/src/main/resources/templates/bash/test/test_helper/common-setup.bash b/src/main/resources/templates/bash/test/test_helper/common-setup.bash new file mode 100644 index 000000000000..23bca120a979 --- /dev/null +++ b/src/main/resources/templates/bash/test/test_helper/common-setup.bash @@ -0,0 +1,34 @@ +_common_setup() { + bats_load_library "bats-support" + bats_load_library "bats-assert" + bats_load_library "bats-file" + + PROJECT_ROOT="$( cd "$BATS_TEST_DIRNAME/.." >/dev/null 2>&1 && pwd )" + ASSIGNMENT_ROOT="$PROJECT_ROOT/${studentParentWorkingDirectoryName}" + + PATH="$ASSIGNMENT_ROOT:$PATH" +} + +# _assert_file_contents +# ============ +# +# Fail if the actual and expected file contents differ. +# +# Usage: _assert_file_contents +# +# IO: +# STDERR - unified diff, on failure +# Options: +# The file being compared. +# The file to compare against. +_assert_file_contents() { + if ! diff_output=$(diff -u --label="actual" --label="expected" "$1" "$2" 2>&1); then + echo "$diff_output" \ + | batslib_decorate "$1: file contents differ" \ + | fail + fi +} + +# reduce output +bats_print_stack_trace() { :; } +bats_print_failed_command() { :; } diff --git a/src/main/resources/templates/jenkins/bash/regularRuns/pipeline.groovy b/src/main/resources/templates/jenkins/bash/regularRuns/pipeline.groovy new file mode 100644 index 000000000000..da257affb46e --- /dev/null +++ b/src/main/resources/templates/jenkins/bash/regularRuns/pipeline.groovy @@ -0,0 +1,55 @@ +/* + * This file configures the actual build steps for the automatic grading. + * + * !!! + * For regular exercises, there is no need to make changes to this file. + * Only this base configuration is actively supported by the Artemis maintainers + * and/or your Artemis instance administrators. + * !!! + */ + +dockerImage = '#dockerImage' +dockerFlags = '#dockerArgs' + +/** + * Main function called by Jenkins. + */ +void testRunner() { + docker.image(dockerImage).inside(dockerFlags) { c -> + runTestSteps() + } +} + +private void runTestSteps() { + test() +} + +/** + * Run unit tests + */ +private void test() { + stage('Set Permissions') { + sh 'find ./assignment -type f -exec chmod +x "{}" +' + } + stage('Create Results Directory') { + sh 'mkdir results' + } + stage('Test') { + sh 'bats --report-formatter junit --output results ./tests || true' + } +} + +/** + * Script of the post build tasks aggregating all JUnit files in $WORKSPACE/results. + * + * Called by Jenkins. + */ +void postBuildTasks() { + sh ''' + sed -i 's/[^[:print:]\t]/�/g' $WORKSPACE/results/*.xml || true + ''' +} + +// very important, do not remove +// required so that Jenkins finds the methods defined in this script +return this diff --git a/src/main/webapp/app/entities/programming/programming-exercise.model.ts b/src/main/webapp/app/entities/programming/programming-exercise.model.ts index bd9246f8569e..ece9e6d6989c 100644 --- a/src/main/webapp/app/entities/programming/programming-exercise.model.ts +++ b/src/main/webapp/app/entities/programming/programming-exercise.model.ts @@ -14,6 +14,7 @@ import dayjs from 'dayjs/esm'; export enum ProgrammingLanguage { EMPTY = 'EMPTY', ASSEMBLER = 'ASSEMBLER', + BASH = 'BASH', C = 'C', C_PLUS_PLUS = 'C_PLUS_PLUS', C_SHARP = 'C_SHARP', diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml index f730d6102f9c..869514c89759 100644 --- a/src/test/resources/config/application.yml +++ b/src/test/resources/config/application.yml @@ -72,6 +72,8 @@ artemis: default: "~~invalid~~" r: default: "~~invalid~~" + bash: + default: "~~invalid~~" c_plus_plus: default: "~~invalid~~" c_sharp: From c832f96c1fc8d303a9f4e35696dc2c488e673905 Mon Sep 17 00:00:00 2001 From: Simon Entholzer <33342534+SimonEntholzer@users.noreply.github.com> Date: Sun, 12 Jan 2025 11:36:10 +0100 Subject: [PATCH 05/16] Development: Improve git access operations performance (#9809) --- ...xerciseStudentParticipationRepository.java | 50 +- ...ammingExerciseParticipationRepository.java | 6 - ...ammingExerciseParticipationRepository.java | 8 +- .../repository/VcsAccessLogRepository.java | 31 +- ...ogrammingExerciseParticipationService.java | 152 +----- .../service/RepositoryService.java | 20 +- .../localvc/ArtemisGitServletService.java | 2 + .../localvc/LocalVCFetchPreUploadHook.java | 3 +- .../localvc/LocalVCFetchPreUploadHookSSH.java | 8 +- .../service/localvc/LocalVCPostPushHook.java | 25 +- .../service/localvc/LocalVCPrePushHook.java | 2 +- .../service/localvc/LocalVCPushFilter.java | 8 - .../localvc/LocalVCServletService.java | 455 +++++++++++------- .../SshGitLocationResolverService.java | 6 +- .../service/localvc/VcsAccessLogService.java | 54 ++- .../service/localvc/ssh/SshConstants.java | 9 + .../service/localvc/ssh/SshGitCommand.java | 4 +- .../web/repository/RepositoryResource.java | 6 +- ...ngExerciseParticipationTestRepository.java | 24 + .../util/ProgrammingExerciseUtilService.java | 10 +- .../util/ProgrammingUtilTestService.java | 4 +- ...ctSpringIntegrationLocalCILocalVCTest.java | 4 +- 22 files changed, 465 insertions(+), 426 deletions(-) create mode 100644 src/test/java/de/tum/cit/aet/artemis/programming/test_repository/TemplateProgrammingExerciseParticipationTestRepository.java diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java index a126934267ae..1ae8a58dc754 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/ProgrammingExerciseStudentParticipationRepository.java @@ -78,8 +78,11 @@ Optional findByIdWithAllResultsAndRelat List findAllByExerciseIdAndStudentLogin(long exerciseId, String username); - default ProgrammingExerciseStudentParticipation findByExerciseIdAndStudentLoginOrThrow(long exerciseId, String username) { - return getValueElseThrow(findByExerciseIdAndStudentLogin(exerciseId, username)); + @EntityGraph(type = LOAD, attributePaths = { "submissions" }) + Optional findWithSubmissionsByRepositoryUri(String repositoryUri); + + default ProgrammingExerciseStudentParticipation findWithSubmissionsByRepositoryUriElseThrow(String repositoryUri) { + return getValueElseThrow(findWithSubmissionsByRepositoryUri(repositoryUri)); } Optional findByRepositoryUri(String repositoryUri); @@ -88,41 +91,9 @@ default ProgrammingExerciseStudentParticipation findByRepositoryUriElseThrow(Str return getValueElseThrow(findByRepositoryUri(repositoryUri)); } - @EntityGraph(type = LOAD, attributePaths = { "submissions" }) - Optional findWithSubmissionsByExerciseIdAndStudentLogin(long exerciseId, String username); - - default ProgrammingExerciseStudentParticipation findWithSubmissionsByExerciseIdAndStudentLoginOrThrow(long exerciseId, String username) { - return getValueElseThrow(findWithSubmissionsByExerciseIdAndStudentLogin(exerciseId, username)); - } - - Optional findByExerciseIdAndStudentLoginAndTestRun(long exerciseId, String username, boolean testRun); - @EntityGraph(type = LOAD, attributePaths = { "team.students" }) Optional findByExerciseIdAndTeamId(long exerciseId, long teamId); - @Query(""" - SELECT DISTINCT participation - FROM ProgrammingExerciseStudentParticipation participation - LEFT JOIN FETCH participation.team team - LEFT JOIN FETCH team.students - WHERE participation.exercise.id = :exerciseId - AND participation.team.shortName = :teamShortName - """) - Optional findWithEagerStudentsByExerciseIdAndTeamShortName(@Param("exerciseId") long exerciseId, - @Param("teamShortName") String teamShortName); - - @Query(""" - SELECT DISTINCT participation - FROM ProgrammingExerciseStudentParticipation participation - LEFT JOIN FETCH participation.submissions - LEFT JOIN FETCH participation.team team - LEFT JOIN FETCH team.students - WHERE participation.exercise.id = :exerciseId - AND participation.team.shortName = :teamShortName - """) - Optional findWithSubmissionsAndEagerStudentsByExerciseIdAndTeamShortName(@Param("exerciseId") long exerciseId, - @Param("teamShortName") String teamShortName); - @Query(""" SELECT DISTINCT participation FROM ProgrammingExerciseStudentParticipation participation @@ -159,17 +130,6 @@ Optional findWithSubmissionsAndEagerStu List findWithSubmissionsByExerciseIdAndParticipationIds(@Param("exerciseId") long exerciseId, @Param("participationIds") Collection participationIds); - @Query(""" - SELECT participation - FROM ProgrammingExerciseStudentParticipation participation - LEFT JOIN FETCH participation.submissions - WHERE participation.exercise.id = :exerciseId - AND participation.student.login = :username - AND participation.testRun = :testRun - """) - Optional findWithSubmissionsByExerciseIdAndStudentLoginAndTestRun(@Param("exerciseId") long exerciseId, - @Param("username") String username, @Param("testRun") boolean testRun); - @Query(""" SELECT participation.repositoryUri FROM ProgrammingExerciseStudentParticipation participation diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java index 4297b39f9ea9..2949576a13cb 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/SolutionProgrammingExerciseParticipationRepository.java @@ -43,12 +43,6 @@ public interface SolutionProgrammingExerciseParticipationRepository """) Optional findByBuildPlanIdWithResults(@Param("buildPlanId") String buildPlanId); - Optional findByRepositoryUri(String repositoryUri); - - default SolutionProgrammingExerciseParticipation findByRepositoryUriElseThrow(String repositoryUri) { - return getValueElseThrow(findByRepositoryUri(repositoryUri)); - } - @EntityGraph(type = LOAD, attributePaths = { "results", "submissions", "submissions.results" }) Optional findWithEagerResultsAndSubmissionsByProgrammingExerciseId(long exerciseId); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java index 67795b58ae54..15624a14e31a 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/TemplateProgrammingExerciseParticipationRepository.java @@ -41,11 +41,11 @@ public interface TemplateProgrammingExerciseParticipationRepository """) Optional findByBuildPlanIdWithResults(@Param("buildPlanId") String buildPlanId); - @EntityGraph(type = LOAD, attributePaths = { "results", "submissions" }) - Optional findWithEagerResultsAndSubmissionsByProgrammingExerciseId(long exerciseId); + @EntityGraph(type = LOAD, attributePaths = { "submissions" }) + Optional findWithSubmissionsByRepositoryUri(String repositoryUri); - default TemplateProgrammingExerciseParticipation findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(long exerciseId) { - return getValueElseThrow(findWithEagerResultsAndSubmissionsByProgrammingExerciseId(exerciseId)); + default TemplateProgrammingExerciseParticipation findWithSubmissionsByRepositoryUriElseThrow(String repositoryUri) { + return getValueElseThrow(findWithSubmissionsByRepositoryUri(repositoryUri)); } Optional findByRepositoryUri(String repositoryUri); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/repository/VcsAccessLogRepository.java b/src/main/java/de/tum/cit/aet/artemis/programming/repository/VcsAccessLogRepository.java index 76722cfd5eea..acd9c46fe47f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/repository/VcsAccessLogRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/repository/VcsAccessLogRepository.java @@ -27,21 +27,36 @@ public interface VcsAccessLogRepository extends ArtemisJpaRepository { /** - * Find the access log entry which does not have any commit hash yet + * Retrieves the most recent {@link VcsAccessLog} for a given participation ID. * - * @param participationId The id of the participation the repository belongs to - * @return a log entry belonging to the participationId, which has no commit hash + * @param participationId the ID of the participation to filter by. + * @return an {@link Optional} containing the newest {@link VcsAccessLog}, or empty if none exists. */ @Query(""" - SELECT vcsAccessLog - FROM VcsAccessLog vcsAccessLog - WHERE vcsAccessLog.participation.id = :participationId - ORDER BY vcsAccessLog.timestamp DESC - LIMIT 1 + FROM VcsAccessLog vcsAccessLog + WHERE vcsAccessLog.participation.id = :participationId + ORDER BY vcsAccessLog.id DESC + LIMIT 1 """) Optional findNewestByParticipationId(@Param("participationId") long participationId); + /** + * Retrieves the most recent {@link VcsAccessLog} for a specific repository URI of a participation. + * + * @param repositoryUri the URI of the participation to filter by. + * @return an Optional containing the newest {@link VcsAccessLog} of the participation, or empty if none exists. + */ + @Query(""" + SELECT vcsAccessLog + FROM VcsAccessLog vcsAccessLog + LEFT JOIN TREAT (vcsAccessLog.participation AS ProgrammingExerciseStudentParticipation) participation + WHERE participation.repositoryUri = :repositoryUri + ORDER BY vcsAccessLog.id DESC + LIMIT 1 + """) + Optional findNewestByRepositoryUri(@Param("repositoryUri") String repositoryUri); + /** * Retrieves a list of {@link VcsAccessLog} entities associated with the specified participation ID. * The results are ordered by the log ID in ascending order. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java index bd8db678b5a2..c6fc7c1c5c3f 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseParticipationService.java @@ -22,8 +22,6 @@ import de.tum.cit.aet.artemis.core.domain.User; import de.tum.cit.aet.artemis.core.exception.EntityNotFoundException; import de.tum.cit.aet.artemis.core.exception.VersionControlException; -import de.tum.cit.aet.artemis.core.repository.UserRepository; -import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; import de.tum.cit.aet.artemis.exam.domain.StudentExam; import de.tum.cit.aet.artemis.exercise.domain.Exercise; import de.tum.cit.aet.artemis.exercise.domain.InitializationState; @@ -68,16 +66,9 @@ public class ProgrammingExerciseParticipationService { private final GitService gitService; - private final AuthorizationCheckService authorizationCheckService; - - private final UserRepository userRepository; - - private final AuxiliaryRepositoryService auxiliaryRepositoryService; - public ProgrammingExerciseParticipationService(SolutionProgrammingExerciseParticipationRepository solutionParticipationRepository, TemplateProgrammingExerciseParticipationRepository templateParticipationRepository, ProgrammingExerciseStudentParticipationRepository studentParticipationRepository, - ParticipationRepository participationRepository, TeamRepository teamRepository, GitService gitService, Optional versionControlService, - AuthorizationCheckService authorizationCheckService, UserRepository userRepository, AuxiliaryRepositoryService auxiliaryRepositoryService) { + ParticipationRepository participationRepository, TeamRepository teamRepository, GitService gitService, Optional versionControlService) { this.studentParticipationRepository = studentParticipationRepository; this.solutionParticipationRepository = solutionParticipationRepository; this.templateParticipationRepository = templateParticipationRepository; @@ -85,9 +76,6 @@ public ProgrammingExerciseParticipationService(SolutionProgrammingExercisePartic this.teamRepository = teamRepository; this.versionControlService = versionControlService; this.gitService = gitService; - this.authorizationCheckService = authorizationCheckService; - this.userRepository = userRepository; - this.auxiliaryRepositoryService = auxiliaryRepositoryService; } /** @@ -124,36 +112,6 @@ public TemplateProgrammingExerciseParticipation findTemplateParticipationByProgr return templateParticipation.get(); } - /** - * Tries to retrieve a team participation for the given exercise and team short name. - * - * @param exercise the exercise for which to find a participation. - * @param teamShortName of the team to which the participation belongs. - * @param withSubmissions true if the participation should be fetched with its submissions. - * @return the participation for the given exercise and team. - * @throws EntityNotFoundException if the team participation was not found. - */ - public ProgrammingExerciseStudentParticipation findTeamParticipationByExerciseAndTeamShortNameOrThrow(ProgrammingExercise exercise, String teamShortName, - boolean withSubmissions) { - - Optional participationOptional; - - // It is important to fetch all students of the team here, because the local VC and local CI system use this participation to check if the authenticated user is part of the - // team. - if (withSubmissions) { - participationOptional = studentParticipationRepository.findWithSubmissionsAndEagerStudentsByExerciseIdAndTeamShortName(exercise.getId(), teamShortName); - } - else { - participationOptional = studentParticipationRepository.findWithEagerStudentsByExerciseIdAndTeamShortName(exercise.getId(), teamShortName); - } - - if (participationOptional.isEmpty()) { - throw new EntityNotFoundException("Participation could not be found by exerciseId " + exercise.getId() + " and team short name " + teamShortName); - } - - return participationOptional.get(); - } - /** * Tries to retrieve a student participation for the given team exercise and user * @@ -166,36 +124,6 @@ public Optional findTeamParticipationBy return studentParticipationRepository.findTeamParticipationByExerciseIdAndStudentId(exercise.getId(), user.getId()); } - /** - * Tries to retrieve a student participation for the given exercise and username and test run flag. - * - * @param exercise the exercise for which to find a participation. - * @param username of the user to which the participation belongs. - * @param isTestRun true if the participation is a test run participation. - * @param withSubmissions true if the participation should be loaded with its submissions. - * @return the participation for the given exercise and user. - * @throws EntityNotFoundException if there is no participation for the given exercise and user. - */ - @NotNull - public ProgrammingExerciseStudentParticipation findStudentParticipationByExerciseAndStudentLoginAndTestRunOrThrow(ProgrammingExercise exercise, String username, - boolean isTestRun, boolean withSubmissions) { - - Optional participationOptional; - - if (withSubmissions) { - participationOptional = studentParticipationRepository.findWithSubmissionsByExerciseIdAndStudentLoginAndTestRun(exercise.getId(), username, isTestRun); - } - else { - participationOptional = studentParticipationRepository.findByExerciseIdAndStudentLoginAndTestRun(exercise.getId(), username, isTestRun); - } - - if (participationOptional.isEmpty()) { - throw new EntityNotFoundException("Participation could not be found by exerciseId " + exercise.getId() + " and user " + username); - } - - return participationOptional.get(); - } - /** * Tries to retrieve a student participation for the given exercise id and username. * @@ -443,63 +371,28 @@ public void resetRepository(VcsRepositoryUri targetURL, VcsRepositoryUri sourceU } /** - * Get the participation for a given exercise and a repository type or user name. This method is used by the local VC system and by the local CI system to get the - * participation. + * Get the participation for a given repository url and a repository type or user name. This method is used by the local VC system to get the + * participation for logging operations on the repository. * - * @param exercise the exercise for which to get the participation. - * @param repositoryTypeOrUserName the repository type ("exercise", "solution", or "tests") or username (e.g. "artemis_test_user_1") as extracted from the repository URI. - * @param isPracticeRepository whether the repository is a practice repository, i.e. the repository URI contains "-practice-". - * @param withSubmissions whether submissions should be loaded with the participation. This is needed for the local CI system. - * @return the participation. - * @throws EntityNotFoundException if the participation could not be found. + * @param repositoryTypeOrUserName the name of the user or the type of the repository + * @param repositoryURI the participation's repository URL + * @param exercise the exercise the participation belongs to + * @return the participation belonging to the provided repositoryURI and repository type or username */ - public ProgrammingExerciseParticipation retrieveParticipationForRepository(ProgrammingExercise exercise, String repositoryTypeOrUserName, boolean isPracticeRepository, - boolean withSubmissions) { - - boolean isAuxiliaryRepository = auxiliaryRepositoryService.isAuxiliaryRepositoryOfExercise(repositoryTypeOrUserName, exercise); - - // For pushes to the tests repository, the solution repository is built first, and thus we need the solution participation. - // Can possibly be used by auxiliary repositories - if (repositoryTypeOrUserName.equals(RepositoryType.SOLUTION.toString()) || repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString()) || isAuxiliaryRepository) { - if (withSubmissions) { - return solutionParticipationRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(exercise.getId()); - } - else { - return solutionParticipationRepository.findByProgrammingExerciseIdElseThrow(exercise.getId()); - } + public ProgrammingExerciseParticipation fetchParticipationWithSubmissionsByRepository(String repositoryTypeOrUserName, String repositoryURI, ProgrammingExercise exercise) { + var repositoryURL = repositoryURI.replace("/git-upload-pack", "").replace("/git-receive-pack", ""); + if (repositoryTypeOrUserName.equals(RepositoryType.SOLUTION.toString()) || repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString())) { + return solutionParticipationRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(exercise.getId()); } - if (repositoryTypeOrUserName.equals(RepositoryType.TEMPLATE.toString())) { - if (withSubmissions) { - return templateParticipationRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(exercise.getId()); - } - else { - return templateParticipationRepository.findByProgrammingExerciseIdElseThrow(exercise.getId()); - } + return templateParticipationRepository.findWithSubmissionsByRepositoryUriElseThrow(repositoryURL); } + return studentParticipationRepository.findWithSubmissionsByRepositoryUriElseThrow(repositoryURL); - if (exercise.isTeamMode()) { - // The repositoryTypeOrUserName is the team short name. - // For teams, there is no practice participation. - return findTeamParticipationByExerciseAndTeamShortNameOrThrow(exercise, repositoryTypeOrUserName, withSubmissions); - } - - // If the exercise is an exam exercise and the repository's owner is at least an editor, the repository could be a test run repository, or it could be the instructor's - // assignment repository. - // There is no way to tell from the repository URI, and only one participation will be created, even if both are used. - // This participation has "testRun = true" set if the test run was created first, and "testRun = false" set if the instructor's assignment repository was created first. - // If the exercise is an exam exercise, and the repository's owner is at least an editor, get the participation without regard for the testRun flag. - boolean isExamEditorRepository = exercise.isExamExercise() - && authorizationCheckService.isAtLeastEditorForExercise(exercise, userRepository.getUserByLoginElseThrow(repositoryTypeOrUserName)); - if (isExamEditorRepository) { - if (withSubmissions) { - return studentParticipationRepository.findWithSubmissionsByExerciseIdAndStudentLoginOrThrow(exercise.getId(), repositoryTypeOrUserName); - } - - return studentParticipationRepository.findByExerciseIdAndStudentLoginOrThrow(exercise.getId(), repositoryTypeOrUserName); - } + } - return findStudentParticipationByExerciseAndStudentLoginAndTestRunOrThrow(exercise, repositoryTypeOrUserName, isPracticeRepository, withSubmissions); + public ProgrammingExerciseParticipation retrieveSolutionParticipation(Exercise exercise) { + return solutionParticipationRepository.findByProgrammingExerciseIdElseThrow(exercise.getId()); } /** @@ -508,19 +401,18 @@ public ProgrammingExerciseParticipation retrieveParticipationForRepository(Progr * * @param repositoryTypeOrUserName the name of the user or the type of the repository * @param repositoryURI the participation's repository URL + * @param exercise the exercise the participation belongs to * @return the participation belonging to the provided repositoryURI and repository type or username */ - public ProgrammingExerciseParticipation retrieveParticipationForRepository(String repositoryTypeOrUserName, String repositoryURI) { + public ProgrammingExerciseParticipation fetchParticipationByRepository(String repositoryTypeOrUserName, String repositoryURI, ProgrammingExercise exercise) { + var repositoryURL = repositoryURI.replace("/git-upload-pack", "").replace("/git-receive-pack", ""); if (repositoryTypeOrUserName.equals(RepositoryType.SOLUTION.toString()) || repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString())) { - return solutionParticipationRepository.findByRepositoryUriElseThrow(repositoryURI); + return solutionParticipationRepository.findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(exercise.getId()); } if (repositoryTypeOrUserName.equals(RepositoryType.TEMPLATE.toString())) { - return templateParticipationRepository.findByRepositoryUriElseThrow(repositoryURI); - } - if (repositoryTypeOrUserName.equals(RepositoryType.AUXILIARY.toString())) { - throw new EntityNotFoundException("Auxiliary repositories do not have participations."); + return templateParticipationRepository.findByRepositoryUriElseThrow(repositoryURL); } - return studentParticipationRepository.findByRepositoryUriElseThrow(repositoryURI); + return studentParticipationRepository.findByRepositoryUriElseThrow(repositoryURL); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/RepositoryService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/RepositoryService.java index 9833a9ae040b..1ca095cac600 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/RepositoryService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/RepositoryService.java @@ -45,6 +45,7 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; import de.tum.cit.aet.artemis.programming.domain.Repository; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; +import de.tum.cit.aet.artemis.programming.domain.VcsAccessLog; import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri; import de.tum.cit.aet.artemis.programming.dto.FileMove; import de.tum.cit.aet.artemis.programming.service.localvc.VcsAccessLogService; @@ -451,15 +452,24 @@ public void pullChanges(Repository repository) { * * @param repository for which to execute the commit. * @param user the user who has committed the changes in the online editor - * @param domainId the id of the domain Object (participation) owning the repository * @throws GitAPIException if the staging/committing process fails. */ - public void commitChanges(Repository repository, User user, Long domainId) throws GitAPIException { + public void commitChanges(Repository repository, User user) throws GitAPIException { gitService.stageAllChanges(repository); gitService.commitAndPush(repository, "Changes by Online Editor", true, user); - if (vcsAccessLogService.isPresent()) { - vcsAccessLogService.get().storeCodeEditorAccessLog(repository, user, domainId); - } + } + + /** + * Saves a preliminary access log for a push from the code editor, and returns it + * + * @param repository for which to execute the commit. + * @param user the user who has committed the changes in the online editor + * @param domainId the id that serves as an abstract identifier for retrieving the repository. + * @return an Optional of a preliminary VcsAccessLog + * @throws GitAPIException if accessing the repository fails + */ + public Optional savePreliminaryCodeEditorAccessLog(Repository repository, User user, Long domainId) throws GitAPIException { + return vcsAccessLogService.isPresent() ? vcsAccessLogService.get().createPreliminaryCodeEditorAccessLog(repository, user, domainId) : Optional.empty(); } /** diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ArtemisGitServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ArtemisGitServletService.java index 0bfbbbe700e0..6f31748488f2 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ArtemisGitServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ArtemisGitServletService.java @@ -58,6 +58,7 @@ public void init() { this.setReceivePackFactory((request, repository) -> { ReceivePack receivePack = new ReceivePack(repository); // Add a hook that prevents illegal actions on push (delete branch, rename branch, force push). + // the user inside the request is always null here receivePack.setPreReceiveHook(new LocalVCPrePushHook(localVCServletService, (User) request.getAttribute("user"))); // Add a hook that triggers the creation of a new submission after the push went through successfully. receivePack.setPostReceiveHook(new LocalVCPostPushHook(localVCServletService)); @@ -72,4 +73,5 @@ public void init() { return uploadPack; }); } + } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java index 97e7523991d7..fc2ad46c5647 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHook.java @@ -21,7 +21,8 @@ public LocalVCFetchPreUploadHook(LocalVCServletService localVCServletService, Ht @Override public void onBeginNegotiateRound(UploadPack uploadPack, Collection collection, int clientOffered) { - localVCServletService.updateVCSAccessLogForCloneAndPullHTTPS(request, clientOffered); + String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); + localVCServletService.updateAndStoreVCSAccessLogForCloneAndPullHTTPS(request, authorizationHeader, clientOffered); } @Override diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java index 09f79348c180..7b2a5465cc97 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCFetchPreUploadHookSSH.java @@ -1,6 +1,5 @@ package de.tum.cit.aet.artemis.programming.service.localvc; -import java.nio.file.Path; import java.util.Collection; import org.apache.sshd.server.session.ServerSession; @@ -14,17 +13,14 @@ public class LocalVCFetchPreUploadHookSSH implements PreUploadHook { private final ServerSession serverSession; - private final Path rootDir; - - public LocalVCFetchPreUploadHookSSH(LocalVCServletService localVCServletService, ServerSession serverSession, Path rootDir) { + public LocalVCFetchPreUploadHookSSH(LocalVCServletService localVCServletService, ServerSession serverSession) { this.localVCServletService = localVCServletService; this.serverSession = serverSession; - this.rootDir = rootDir; } @Override public void onBeginNegotiateRound(UploadPack uploadPack, Collection collection, int clientOffered) { - localVCServletService.updateVCSAccessLogForCloneAndPullSSH(serverSession, rootDir, clientOffered); + localVCServletService.updateAndStoreVCSAccessLogForCloneAndPullSSH(serverSession, clientOffered); } @Override diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPostPushHook.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPostPushHook.java index 024c0fa77546..f0529c7bc33e 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPostPushHook.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPostPushHook.java @@ -2,7 +2,9 @@ import java.util.Collection; import java.util.Iterator; +import java.util.Optional; +import org.apache.sshd.server.session.ServerSession; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.transport.PostReceiveHook; import org.eclipse.jgit.transport.ReceiveCommand; @@ -10,6 +12,10 @@ import de.tum.cit.aet.artemis.core.exception.LocalCIException; import de.tum.cit.aet.artemis.core.exception.VersionControlException; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; +import de.tum.cit.aet.artemis.programming.domain.VcsAccessLog; +import de.tum.cit.aet.artemis.programming.service.localvc.ssh.SshConstants; /** * Contains an onPostReceive method that is called by JGit after a push has been received (i.e. after the pushed files were successfully written to disk). @@ -18,8 +24,25 @@ public class LocalVCPostPushHook implements PostReceiveHook { private final LocalVCServletService localVCServletService; + private final ProgrammingExerciseParticipation participation; + + private final ProgrammingExercise exercise; + + private final VcsAccessLog vcsAccessLog; + + public LocalVCPostPushHook(LocalVCServletService localVCServletService, ServerSession serverSession) { + this.localVCServletService = localVCServletService; + this.participation = serverSession.getAttribute(SshConstants.PARTICIPATION_KEY); + this.exercise = serverSession.getAttribute(SshConstants.REPOSITORY_EXERCISE_KEY); + this.vcsAccessLog = serverSession.getAttribute(SshConstants.VCS_ACCESS_LOG_KEY); + } + public LocalVCPostPushHook(LocalVCServletService localVCServletService) { this.localVCServletService = localVCServletService; + // For HTTPs we are unable to store the attributes in the session or request unfortunately + this.participation = null; + this.exercise = null; + this.vcsAccessLog = null; } /** @@ -57,7 +80,7 @@ public void onPostReceive(ReceivePack receivePack, Collection co Repository repository = receivePack.getRepository(); try { - localVCServletService.processNewPush(commitHash, repository); + localVCServletService.processNewPush(commitHash, repository, Optional.ofNullable(exercise), Optional.ofNullable(participation), Optional.ofNullable(vcsAccessLog)); } catch (LocalCIException e) { // Return an error message to the user. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPrePushHook.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPrePushHook.java index 9c4eb9a9e128..9a6df5eea2de 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPrePushHook.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPrePushHook.java @@ -58,7 +58,7 @@ public void onPreReceive(ReceivePack receivePack, Collection com String defaultBranchName; try { - defaultBranchName = LocalVCServletService.getDefaultBranchOfRepository(repository); + defaultBranchName = LocalVCService.getDefaultBranchOfRepository(repository.getDirectory().toPath().toString()); } catch (LocalVCInternalException e) { command.setResult(ReceiveCommand.Result.REJECTED_OTHER_REASON, "An error occurred while checking the branch."); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java index df082b7362c2..6237412fefa0 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCPushFilter.java @@ -48,14 +48,6 @@ public void doFilterInternal(HttpServletRequest servletRequest, HttpServletRespo return; } - // We need to extract the content of the request here as it is garbage collected before it can be used asynchronously - String authorizationHeader = servletRequest.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); - String method = servletRequest.getMethod(); - LocalVCRepositoryUri localVCRepositoryUri = localVCServletService.parseRepositoryUri(servletRequest); - - this.localVCServletService.updateVCSAccessLogForPushHTTPS(method, authorizationHeader, localVCRepositoryUri); - filterChain.doFilter(servletRequest, servletResponse); - } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java index 986f3837b7af..bb33dfc715cf 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCServletService.java @@ -16,7 +16,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import jakarta.servlet.http.HttpServletRequest; @@ -34,7 +33,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.http.HttpStatus; -import org.springframework.scheduling.annotation.Async; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.AuthenticationException; @@ -52,6 +50,7 @@ import de.tum.cit.aet.artemis.core.security.SecurityUtils; import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService; import de.tum.cit.aet.artemis.core.util.TimeLogUtil; +import de.tum.cit.aet.artemis.exercise.domain.participation.Participation; import de.tum.cit.aet.artemis.programming.domain.AuthenticationMechanism; import de.tum.cit.aet.artemis.programming.domain.Commit; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; @@ -60,6 +59,7 @@ import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; import de.tum.cit.aet.artemis.programming.domain.SolutionProgrammingExerciseParticipation; +import de.tum.cit.aet.artemis.programming.domain.VcsAccessLog; import de.tum.cit.aet.artemis.programming.repository.ParticipationVCSAccessTokenRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseRepository; import de.tum.cit.aet.artemis.programming.service.AuxiliaryRepositoryService; @@ -220,6 +220,11 @@ public void authenticateAndAuthorizeGitRequest(HttpServletRequest request, Repos String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); + // The first request does not contain an authorizationHeader, the client expects this response + if (authorizationHeader == null) { + throw new LocalVCAuthException("No authorization header provided"); + } + // If it is a fetch request, we check if it is the build agent that is fetching the repository. if (repositoryAction == RepositoryActionType.READ) { UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); @@ -251,15 +256,43 @@ public void authenticateAndAuthorizeGitRequest(HttpServletRequest request, Repos throw new LocalVCForbiddenException(); } - var authenticationMechanism = resolveAuthenticationMechanism(authorizationHeader, user); - var ipAddress = request.getRemoteAddr(); - authorizeUser(repositoryTypeOrUserName, user, exercise, repositoryAction, authenticationMechanism, ipAddress, localVCRepositoryUri); - - request.setAttribute("user", user); + var optionalParticipation = authorizeUser(repositoryTypeOrUserName, user, exercise, repositoryAction, localVCRepositoryUri, false); + savePreliminaryVcsAccessLogForHTTPs(request, localVCRepositoryUri, user, repositoryAction, optionalParticipation); log.debug("Authorizing user {} for repository {} took {}", user.getLogin(), localVCRepositoryUri, TimeLogUtil.formatDurationFrom(timeNanoStart)); } + /** + * Determines whether a given request to access a local VC repository (either via fetch of push) is authenticated and authorized. + * + * @param request The object containing all information about the incoming request. + * @param localVCRepositoryUri The uri of the requested repository + * @param user The user + * @param repositoryAction Indicates whether the method should authenticate a fetch or a push request. For a push request, additional checks are conducted. + * @param optionalParticipation The participation for which the access log should be stored. If an empty Optional is provided, the method does nothing + * @throws LocalVCAuthException If the user authentication fails or the user is not authorized to access a certain repository. + */ + private void savePreliminaryVcsAccessLogForHTTPs(HttpServletRequest request, LocalVCRepositoryUri localVCRepositoryUri, User user, RepositoryActionType repositoryAction, + Optional optionalParticipation) throws LocalVCAuthException { + if (optionalParticipation.isPresent()) { + ProgrammingExerciseParticipation participation = optionalParticipation.get(); + var ipAddress = request.getRemoteAddr(); + var authenticationMechanism = resolveHTTPSAuthenticationMechanism(request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER), user); + + String commitHash = null; + try { + commitHash = getLatestCommitHash(repositories.get(localVCRepositoryUri.getRelativeRepositoryPath().toString())); + } + catch (GitAPIException e) { + log.warn("Failed to obtain commit hash for repository {}. Error: {}", localVCRepositoryUri.getRelativeRepositoryPath().toString(), e.getMessage()); + } + + String finalCommitHash = commitHash; + RepositoryActionType finalRepositoryAction = repositoryAction == RepositoryActionType.WRITE ? RepositoryActionType.PUSH : RepositoryActionType.PULL; + vcsAccessLogService.ifPresent(service -> service.saveAccessLog(user, participation, finalRepositoryAction, authenticationMechanism, finalCommitHash, ipAddress)); + } + } + /** * Resolves the user's authentication mechanism for the repository * @@ -268,7 +301,7 @@ public void authenticateAndAuthorizeGitRequest(HttpServletRequest request, Repos * @return the authentication type * @throws LocalVCAuthException if extracting the token or password from the authorizationHeader fails */ - private AuthenticationMechanism resolveAuthenticationMechanism(String authorizationHeader, User user) throws LocalVCAuthException { + private AuthenticationMechanism resolveHTTPSAuthenticationMechanism(String authorizationHeader, User user) throws LocalVCAuthException { UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); String password = usernameAndPassword.password(); @@ -281,36 +314,73 @@ private AuthenticationMechanism resolveAuthenticationMechanism(String authorizat return AuthenticationMechanism.PARTICIPATION_VCS_ACCESS_TOKEN; } + /** + * Authenticates a user based on the provided authorization header for a specific programming exercise/repository. + * Authentication is tried with: 1) user VCS access token, 2) user participation VCS access token 3) password + * + * @param authorizationHeader the authorization header containing authentication credentials + * @param exercise the programming exercise the user is attempting to access + * @param localVCRepositoryUri the URI of the local version control repository the user is attempting to access + * + * @return the authenticated {@link User} if authentication is successful + * @throws LocalVCAuthException if an error occurs during authentication with the local version control system + * @throws AuthenticationException if the authentication credentials are invalid or authentication fails + */ private User authenticateUser(String authorizationHeader, ProgrammingExercise exercise, LocalVCRepositoryUri localVCRepositoryUri) throws LocalVCAuthException, AuthenticationException { UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); - String username = usernameAndPassword.username(); - String password = usernameAndPassword.password(); + String passwordOrToken = usernameAndPassword.password(); + User user = userRepository.findOneByLogin(username).orElseThrow(LocalVCAuthException::new); try { - SecurityUtils.checkUsernameAndPasswordValidity(username, password); + SecurityUtils.checkUsernameAndPasswordValidity(username, passwordOrToken); } catch (AccessForbiddenException | AuthenticationException e) { - if (!StringUtils.isEmpty(password)) { - log.warn("Failed login attempt for user {} with password {} due to issue: {}", username, password, e.getMessage()); + if (StringUtils.isNotEmpty(passwordOrToken)) { + log.warn("Failed login attempt for user {} with password {} due to issue: {}", username, passwordOrToken, e.getMessage()); } throw new LocalVCAuthException(e.getMessage()); } // check user VCS access token - if (Objects.equals(user.getVcsAccessToken(), password) && user.getVcsAccessTokenExpiryDate() != null && user.getVcsAccessTokenExpiryDate().isAfter(ZonedDateTime.now())) { + if (Objects.equals(user.getVcsAccessToken(), passwordOrToken) && user.getVcsAccessTokenExpiryDate() != null + && user.getVcsAccessTokenExpiryDate().isAfter(ZonedDateTime.now())) { + return user; + } + + // check user participation VCS access token + if (tryAuthenticationWithParticipationVCSAccessToken(user, passwordOrToken, exercise, localVCRepositoryUri)) { return user; } + // if the user does not have an access token or used a password, we try to authenticate the user with it + // Try to authenticate the user based on the configured options, this can include sending the data to an external system (e.g. LDAP) or using internal authentication. + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, passwordOrToken); + authenticationManager.authenticate(authenticationToken); + + return user; + } + + /** + * Attempts to authenticate a user with the provided participation VCS access token + * + * @param user the user attempting authentication + * @param providedToken the participation VCS access token provided by the user + * @param exercise the programming exercise containing the repository the user tries to access + * @param localVCRepositoryUri the URI of the local version control repository the user tries to access + * @return {@code true} if the authentication is successful, {@code false} otherwise + */ + private boolean tryAuthenticationWithParticipationVCSAccessToken(User user, String providedToken, ProgrammingExercise exercise, LocalVCRepositoryUri localVCRepositoryUri) + throws LocalVCAuthException { + // Note: we first check if the user has used a vcs access token instead of a password - if (password.startsWith(TOKEN_PREFIX) && password.length() == VCS_ACCESS_TOKEN_LENGTH) { + if (providedToken.startsWith(TOKEN_PREFIX) && providedToken.length() == VCS_ACCESS_TOKEN_LENGTH) { try { // check participation vcs access token - // var part = programmingExerciseParticipationService.findTeamParticipationByExerciseAndTeamShortNameOrThrow() List participations; Optional studentParticipation; if (exercise.isTeamMode()) { @@ -321,10 +391,10 @@ private User authenticateUser(String authorizationHeader, ProgrammingExercise ex studentParticipation = participations.stream().filter(participation -> participation.getRepositoryUri().equals(localVCRepositoryUri.toString())).findAny(); } if (studentParticipation.isPresent()) { - var token = participationVCSAccessTokenRepository.findByUserIdAndParticipationId(user.getId(), studentParticipation.get().getId()); - if (token.isPresent() && Objects.equals(token.get().getVcsAccessToken(), password)) { - user.setVcsAccessToken(token.get().getVcsAccessToken()); - return user; + var storedToken = participationVCSAccessTokenRepository.findByUserIdAndParticipationId(user.getId(), studentParticipation.get().getId()); + if (storedToken.isPresent() && Objects.equals(storedToken.get().getVcsAccessToken(), providedToken)) { + user.setVcsAccessToken(storedToken.get().getVcsAccessToken()); + return true; } } } @@ -332,14 +402,7 @@ private User authenticateUser(String authorizationHeader, ProgrammingExercise ex throw new LocalVCAuthException(); } } - - // if the user does not have an access token or has used a password, we try to authenticate the user with it - - // Try to authenticate the user based on the configured options, this can include sending the data to an external system (e.g. LDAP) or using internal authentication. - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); - authenticationManager.authenticate(authenticationToken); - - return user; + return false; } /** @@ -379,23 +442,26 @@ private ProgrammingExercise getProgrammingExerciseOrThrow(String projectKey) { } } - private String checkAuthorizationHeader(String authorizationHeader) throws LocalVCAuthException { + /** + * Extracts the username and password from a Basic Authorization header. + * + * @param authorizationHeader the authorization header containing Basic credentials + * @return a {@link UsernameAndPassword} object with the extracted username and password + * @throws LocalVCAuthException if the header is missing, invalid, or improperly formatted + */ + private UsernameAndPassword extractUsernameAndPassword(String authorizationHeader) throws LocalVCAuthException { if (authorizationHeader == null) { throw new LocalVCAuthException("No authorization header provided"); } - String[] basicAuthCredentialsEncoded = authorizationHeader.split(" "); if (!("Basic".equals(basicAuthCredentialsEncoded[0]))) { - throw new LocalVCAuthException(); + throw new LocalVCAuthException("Non basic authorization header provided"); } // Return decoded basic auth credentials which contain the username and the password. - return new String(Base64.getDecoder().decode(basicAuthCredentialsEncoded[1])); - } + String basicAuthCredentials = new String(Base64.getDecoder().decode(basicAuthCredentialsEncoded[1])); - private UsernameAndPassword extractUsernameAndPassword(String authorizationHeader) throws LocalVCAuthException { - String basicAuthCredentials = checkAuthorizationHeader(authorizationHeader); int separatorIndex = basicAuthCredentials.indexOf(":"); if (separatorIndex == -1) { @@ -414,81 +480,136 @@ private UsernameAndPassword extractUsernameAndPassword(String authorizationHeade * @param user The user that wants to access the repository. * @param exercise The exercise the repository belongs to. * @param repositoryActionType The type of the action the user wants to perform. - * @param authenticationMechanism The authentication mechanism used by the user to authenticate to the repository - * @param ipAddress The ip address of the user * @param localVCRepositoryUri The URI of the local repository. - * + * @param usingSSH The flag specifying whether the method is called from the SSH or HTTPs context + * @return the ProgrammingParticipation Optional, containing the participation fetched during authorization * @throws LocalVCForbiddenException If the user is not allowed to access the repository. */ - public void authorizeUser(String repositoryTypeOrUserName, User user, ProgrammingExercise exercise, RepositoryActionType repositoryActionType, - AuthenticationMechanism authenticationMechanism, String ipAddress, LocalVCRepositoryUri localVCRepositoryUri) throws LocalVCForbiddenException { + public Optional authorizeUser(String repositoryTypeOrUserName, User user, ProgrammingExercise exercise, + RepositoryActionType repositoryActionType, LocalVCRepositoryUri localVCRepositoryUri, boolean usingSSH) throws LocalVCForbiddenException { - if (repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString()) || auxiliaryRepositoryService.isAuxiliaryRepositoryOfExercise(repositoryTypeOrUserName, exercise)) { - // Test and auxiliary repositories are only accessible by instructors and higher. - try { - repositoryAccessService.checkAccessTestOrAuxRepositoryElseThrow(repositoryActionType == RepositoryActionType.WRITE, exercise, user, repositoryTypeOrUserName); - } - catch (AccessForbiddenException e) { - throw new LocalVCForbiddenException(e); - } - return; + if (checkIfRepositoryIsAuxiliaryOrTestRepository(exercise, repositoryTypeOrUserName, repositoryActionType, user)) { + return Optional.empty(); } + ProgrammingExerciseParticipation participation = tryToLoadParticipation(usingSSH, repositoryTypeOrUserName, localVCRepositoryUri, exercise); + + checkAccessForRepository(participation, user, exercise, repositoryActionType); + + return Optional.of(participation); + } + + private ProgrammingExerciseParticipation tryToLoadParticipation(boolean usingSSH, String repositoryTypeOrUserName, LocalVCRepositoryUri localVCRepositoryUri, + ProgrammingExercise exercise) throws LocalVCInternalException { ProgrammingExerciseParticipation participation; try { - participation = programmingExerciseParticipationService.retrieveParticipationForRepository(exercise, repositoryTypeOrUserName, - localVCRepositoryUri.isPracticeRepository(), true); - - // TODO Add this back in when we have figured out what is incorrect in the playwright configuration for (MySQL, Local) - // participation = programmingExerciseParticipationService.retrieveParticipationForRepository(repositoryTypeOrUserName, localVCRepositoryUri.toString()); + if (usingSSH) { + participation = programmingExerciseParticipationService.fetchParticipationWithSubmissionsByRepository(repositoryTypeOrUserName, localVCRepositoryUri.toString(), + exercise); + } + else { + participation = programmingExerciseParticipationService.fetchParticipationByRepository(repositoryTypeOrUserName, localVCRepositoryUri.toString(), exercise); + } } catch (EntityNotFoundException e) { + // If the repository was not found, this could mean it is an auxiliary repository (which do not have participations) + if (auxiliaryRepositoryService.isAuxiliaryRepositoryOfExercise(repositoryTypeOrUserName, exercise)) { + return programmingExerciseParticipationService.findSolutionParticipationByProgrammingExerciseId(exercise.getId()); + } throw new LocalVCInternalException( "No participation found for repository with repository type or username " + repositoryTypeOrUserName + " in exercise " + exercise.getId(), e); } + return participation; + } + private void checkAccessForRepository(ProgrammingExerciseParticipation participation, User user, ProgrammingExercise exercise, RepositoryActionType repositoryActionType) + throws LocalVCForbiddenException { try { repositoryAccessService.checkAccessRepositoryElseThrow(participation, user, exercise, repositoryActionType); } catch (AccessForbiddenException e) { throw new LocalVCForbiddenException(e); } + } - // Asynchronously store an VCS access log entry - CompletableFuture.runAsync(() -> storeAccessLogAsync(user, participation, repositoryActionType, authenticationMechanism, ipAddress, localVCRepositoryUri)) - .exceptionally(ex -> { - log.warn("Failed to asynchronously obtain commit hash or store access log for repository {}. Error: {}", localVCRepositoryUri.getRelativeRepositoryPath(), - ex.getMessage()); - return null; - }); + /** + * Checks if the provided repository is an auxiliary or test repository. + * But: for students it only checks for test repository, and assumes the requested repository is not an auxiliary repository. + * This avoids an unnecessary database call, and postpones the actual check to + * {@link LocalVCServletService#tryToLoadParticipation(boolean, String, LocalVCRepositoryUri, ProgrammingExercise)} + * and only checks it if it is really needed. + * + * @param exercise the exercise, where the repository belongs to + * @param repositoryTypeOrUserName the type or username of the repository + * @param repositoryActionType the action that should be performed on of the repository + * @param user the user who tries to access the repository + * @return true if the repository is an Auxiliary or Test repository, and the user has access to it. + * false for students if the repository is possibly an auxiliary repository, or + * false for TAs if the repository is neither auxiliary nor test + * @throws LocalVCForbiddenException if the user has no access rights for the requested repository + */ + private boolean checkIfRepositoryIsAuxiliaryOrTestRepository(ProgrammingExercise exercise, String repositoryTypeOrUserName, RepositoryActionType repositoryActionType, + User user) throws LocalVCForbiddenException { + + // Students are not able to access Test or Aux repositories. + // To save on db queries we do not check whether it is an Aux repo here, as we would need to fetch them first. + if (!authorizationCheckService.isAtLeastTeachingAssistantForExercise(exercise, user)) { + if (repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString())) { + throw new LocalVCForbiddenException(); + } + // The user is a student, and the repository is not a test repository + return false; + } + + // Here we only check if the repository is an auxiliary repository if the user is at least TA. + // Why? If the requested repository is not an auxiliary repo, we do not need to load auxiliary repositories + if (auxiliaryRepositoryService.isAuxiliaryRepositoryOfExercise(repositoryTypeOrUserName, exercise) || repositoryTypeOrUserName.equals(RepositoryType.TESTS.toString())) { + try { + repositoryAccessService.checkAccessTestOrAuxRepositoryElseThrow(repositoryActionType == RepositoryActionType.WRITE, exercise, user, repositoryTypeOrUserName); + } + catch (AccessForbiddenException e) { + throw new LocalVCForbiddenException(e); + } + // The user is at least TA, it is either an Auxiliary repository or a Test repository, and the user has access to it + return true; + } + // The repository is neither an Auxiliary repository nor a Test repository + return false; } /** - * Asynchronously retrieves the latest commit hash from the specified repository and logs the access to the repository. - * This method runs without blocking the user during repository access checks. + * When cloning/pushing with SSH we can keep data loaded inside the SSH session, to avoid unnecessary database queries. * * @param user the user accessing the repository - * @param participation the participation associated with the repository + * @param optionalParticipation the participation associated with the repository * @param repositoryActionType the action performed on the repository (READ or WRITE) * @param authenticationMechanism the mechanism used for authentication (e.g., token, basic auth) * @param ipAddress the IP address of the user accessing the repository * @param localVCRepositoryUri the URI of the localVC repository + * @param serverSession the SSH serverSession, where the data gets stored */ - private void storeAccessLogAsync(User user, ProgrammingExerciseParticipation participation, RepositoryActionType repositoryActionType, - AuthenticationMechanism authenticationMechanism, String ipAddress, LocalVCRepositoryUri localVCRepositoryUri) { - try { - String commitHash; - String relativeRepositoryPath = localVCRepositoryUri.getRelativeRepositoryPath().toString(); - try (Repository repository = resolveRepository(relativeRepositoryPath)) { - commitHash = getLatestCommitHash(repository); - } + public void cacheAttributesInSshSession(User user, Optional optionalParticipation, RepositoryActionType repositoryActionType, + AuthenticationMechanism authenticationMechanism, String ipAddress, LocalVCRepositoryUri localVCRepositoryUri, ServerSession serverSession) { + if (optionalParticipation.isPresent()) { + ProgrammingExerciseParticipation participation = optionalParticipation.get(); + try { + String commitHash; + String relativeRepositoryPath = localVCRepositoryUri.getRelativeRepositoryPath().toString(); + try (Repository repository = resolveRepository(relativeRepositoryPath)) { + commitHash = getLatestCommitHash(repository); + } - RepositoryActionType finalRepositoryActionType = repositoryActionType == RepositoryActionType.READ ? RepositoryActionType.PULL : RepositoryActionType.PUSH; - vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, finalRepositoryActionType, authenticationMechanism, commitHash, ipAddress)); + var finalRepositoryActionType = repositoryActionType == RepositoryActionType.READ ? RepositoryActionType.PULL : RepositoryActionType.PUSH; + var preliminaryAccessLog = new VcsAccessLog(user, (Participation) participation, user.getName(), user.getEmail(), finalRepositoryActionType, + authenticationMechanism, commitHash, ipAddress); - } - catch (Exception e) { - log.warn("Failed to obtain commit hash or store access log for repository {}. Error: {}", localVCRepositoryUri.getRelativeRepositoryPath().toString(), e.getMessage()); + serverSession.setAttribute(SshConstants.VCS_ACCESS_LOG_KEY, preliminaryAccessLog); + serverSession.setAttribute(SshConstants.PARTICIPATION_KEY, participation); + } + catch (Exception e) { + log.warn("Failed to obtain commit hash or store access log for repository {}. Error: {}", localVCRepositoryUri.getRelativeRepositoryPath().toString(), + e.getMessage()); + } } } @@ -521,6 +642,23 @@ else if (exception instanceof LocalVCForbiddenException) { * @throws VersionControlException if the commit belongs to the wrong branch (i.e. not the default branch of the participation). */ public void processNewPush(String commitHash, Repository repository) { + processNewPush(commitHash, repository, Optional.empty(), Optional.empty(), Optional.empty()); + } + + /** + * Create a submission, trigger the respective build, and process the results. + * This method can be called with some values, to avoid loading them again from the database + * + * @param commitHash the hash of the last commit. + * @param repository the remote repository which was pushed to. + * @param cachedExercise the exercise which is potentially already loaded + * @param cachedParticipation the participation which is potentially already loaded + * @param vcsAccessLog the vcsAccessLog which is potentially already loaded + * @throws ContinuousIntegrationException if something goes wrong with the CI configuration. + * @throws VersionControlException if the commit belongs to the wrong branch (i.e. not the default branch of the participation). + */ + public void processNewPush(String commitHash, Repository repository, Optional cachedExercise, + Optional cachedParticipation, Optional vcsAccessLog) { long timeNanoStart = System.nanoTime(); Path repositoryFolderPath = repository.getDirectory().toPath(); @@ -529,12 +667,23 @@ public void processNewPush(String commitHash, Repository repository) { String repositoryTypeOrUserName = localVCRepositoryUri.getRepositoryTypeOrUserName(); String projectKey = localVCRepositoryUri.getProjectKey(); + ProgrammingExercise exercise = cachedExercise.orElseGet(() -> getProgrammingExercise(projectKey)); + ProgrammingExerciseParticipation participation; + RepositoryType repositoryType = getRepositoryTypeWithoutAuxiliary(repositoryTypeOrUserName, exercise); - ProgrammingExercise exercise = getProgrammingExercise(projectKey); - - ProgrammingExerciseParticipation participation = getProgrammingExerciseParticipation(localVCRepositoryUri, repositoryTypeOrUserName, exercise); - - RepositoryType repositoryType = getRepositoryType(repositoryTypeOrUserName, exercise); + try { + participation = cachedParticipation.orElseGet(() -> programmingExerciseParticipationService + .fetchParticipationWithSubmissionsByRepository(localVCRepositoryUri.getRepositoryTypeOrUserName(), localVCRepositoryUri.toString(), exercise)); + } + catch (EntityNotFoundException e) { + repositoryType = getRepositoryType(repositoryTypeOrUserName, exercise); + if (repositoryType.equals(RepositoryType.AUXILIARY) || repositoryType.equals(RepositoryType.TESTS)) { + participation = retrieveSolutionParticipation(exercise); + } + else { + throw new VersionControlException("Could not find participation for repository", e); + } + } try { if (repositoryType.equals(RepositoryType.TESTS)) { @@ -557,9 +706,16 @@ public void processNewPush(String commitHash, Repository repository) { // Process push to any repository other than the test repository. processNewPushToRepository(participation, commit); - // For push the correct commitHash is only available here, therefore the preliminary null value is overwritten + // For push the correct commitHash is only available here, therefore the preliminary value is overwritten String finalCommitHash = commitHash; - vcsAccessLogService.ifPresent(service -> service.updateCommitHash(participation, finalCommitHash)); + if (vcsAccessLog.isPresent()) { + vcsAccessLog.get().setCommitHash(finalCommitHash); + vcsAccessLogService.ifPresent(service -> service.saveVcsAccesslog(vcsAccessLog.get())); + } + else { + var finalParticipation = participation; + vcsAccessLogService.ifPresent(service -> service.updateCommitHash(finalParticipation, finalCommitHash)); + } } catch (GitAPIException | IOException e) { // This catch clause does not catch exceptions that happen during runBuildJob() as that method is called asynchronously. @@ -572,17 +728,8 @@ public void processNewPush(String commitHash, Repository repository) { TimeLogUtil.formatDurationFrom(timeNanoStart)); } - private ProgrammingExerciseParticipation getProgrammingExerciseParticipation(LocalVCRepositoryUri localVCRepositoryUri, String repositoryTypeOrUserName, - ProgrammingExercise exercise) { - ProgrammingExerciseParticipation participation; - try { - participation = programmingExerciseParticipationService.retrieveParticipationForRepository(exercise, repositoryTypeOrUserName, - localVCRepositoryUri.isPracticeRepository(), true); - } - catch (EntityNotFoundException e) { - throw new VersionControlException("Could not find participation for repository " + repositoryTypeOrUserName + " of exercise " + exercise, e); - } - return participation; + private ProgrammingExerciseParticipation retrieveSolutionParticipation(ProgrammingExercise exercise) { + return programmingExerciseParticipationService.retrieveSolutionParticipation(exercise); } private ProgrammingExercise getProgrammingExercise(String projectKey) { @@ -673,6 +820,21 @@ else if (auxiliaryRepositoryService.isAuxiliaryRepositoryOfExercise(repositoryTy } } + private RepositoryType getRepositoryTypeWithoutAuxiliary(String repositoryTypeOrUserName, ProgrammingExercise exercise) { + if (repositoryTypeOrUserName.equals("exercise")) { + return RepositoryType.TEMPLATE; + } + else if (repositoryTypeOrUserName.equals("solution")) { + return RepositoryType.SOLUTION; + } + else if (repositoryTypeOrUserName.equals("tests")) { + return RepositoryType.TESTS; + } + else { + return RepositoryType.USER; + } + } + /** * TODO: this could be done asynchronously to shorten the duration of the push operation * Process a new push to a student's repository or to the template or solution repository of the exercise. @@ -728,40 +890,6 @@ private Commit extractCommitInfo(String commitHash, Repository repository) throw return new Commit(commitHash, author.getName(), revCommit.getFullMessage(), author.getEmailAddress(), branch); } - /** - * Retrieves the participation for a programming exercise based on the repository URI. - * - * @param localVCRepositoryUri the {@link LocalVCRepositoryUri} containing details about the repository. - * @return the {@link ProgrammingExerciseParticipation} corresponding to the repository URI. - */ - private ProgrammingExerciseParticipation retrieveParticipationFromLocalVCRepositoryUri(LocalVCRepositoryUri localVCRepositoryUri) { - String repositoryTypeOrUserName = localVCRepositoryUri.getRepositoryTypeOrUserName(); - var repositoryURL = localVCRepositoryUri.toString().replace("/git-upload-pack", "").replace("/git-receive-pack", ""); - return programmingExerciseParticipationService.retrieveParticipationForRepository(repositoryTypeOrUserName, repositoryURL); - } - - /** - * Retrieves the participation for a programming exercise based on the HTTP request. - * - * @param request the {@link HttpServletRequest} containing the repository URI. - * @return the {@link ProgrammingExerciseParticipation} corresponding to the repository details in the request. - */ - private ProgrammingExerciseParticipation getExerciseParticipationFromRequest(HttpServletRequest request) { - LocalVCRepositoryUri localVCRepositoryUri = parseRepositoryUri(request); - return retrieveParticipationFromLocalVCRepositoryUri(localVCRepositoryUri); - } - - /** - * Determine the default branch of the given repository. - * - * @param repository the repository for which the default branch should be determined. - * @return the name of the default branch. - */ - public static String getDefaultBranchOfRepository(Repository repository) { - Path repositoryFolderPath = repository.getDirectory().toPath(); - return LocalVCService.getDefaultBranchOfRepository(repositoryFolderPath.toString()); - } - /** * Updates the VCS (Version Control System) access log for clone and pull actions using HTTPS. *

@@ -769,44 +897,13 @@ public static String getDefaultBranchOfRepository(Repository repository) { * is performed by a build job user and, if not, records the user's repository action (clone or pull). * The action type is determined based on the number of offers (`clientOffered`). * - * @param request the {@link HttpServletRequest} containing the HTTP request data, including headers. - * @param clientOffered the number of objects offered by the client in the operation, used to determine - * if the action is a clone (if 0) or a pull (if greater than 0). - */ - @Async - public void updateVCSAccessLogForCloneAndPullHTTPS(HttpServletRequest request, int clientOffered) { - try { - String authorizationHeader = request.getHeader(LocalVCServletService.AUTHORIZATION_HEADER); - UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); - String userName = usernameAndPassword.username(); - if (userName.equals(BUILD_USER_NAME)) { - return; - } - RepositoryActionType repositoryActionType = getRepositoryActionReadType(clientOffered); - var participation = getExerciseParticipationFromRequest(request); - - vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(participation, repositoryActionType)); - } - catch (Exception ignored) { - } - } - - /** - * Updates the VCS access log for a push action using HTTPS. - *

- * This method logs the access information if the HTTP request is a POST request and the action - * is not performed by a build job user. The repository action type is set as a push action. - * - * @param method the HTTP method of the request (expected to be "POST" for logging to occur) - * @param authorizationHeader the authorization header containing the username and password in basic authentication format - * @param localVcUri the {@link LocalVCRepositoryUri} identifying the repository in the local version control system - * - * This method is asynchronous. - * + * @param request the request from the user + * @param authorizationHeader the authorization header containing the user's credentials + * @param clientOffered the number of objects offered by the client in the operation, used to determine + * if the action is a clone (if 0) or a pull (if greater than 0). */ - @Async - public void updateVCSAccessLogForPushHTTPS(String method, String authorizationHeader, LocalVCRepositoryUri localVcUri) { - if (!method.equals("POST")) { + public void updateAndStoreVCSAccessLogForCloneAndPullHTTPS(HttpServletRequest request, String authorizationHeader, int clientOffered) { + if (!request.getMethod().equals("POST")) { return; } try { @@ -815,10 +912,10 @@ public void updateVCSAccessLogForPushHTTPS(String method, String authorizationHe if (userName.equals(BUILD_USER_NAME)) { return; } - RepositoryActionType repositoryActionType = RepositoryActionType.PUSH; - var participation = retrieveParticipationFromLocalVCRepositoryUri(localVcUri); + LocalVCRepositoryUri localVCRepositoryUri = parseRepositoryUri(request); + RepositoryActionType repositoryActionType = getRepositoryActionReadType(clientOffered); - vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(participation, repositoryActionType)); + vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(localVCRepositoryUri, repositoryActionType)); } catch (Exception ignored) { } @@ -832,19 +929,18 @@ public void updateVCSAccessLogForPushHTTPS(String method, String authorizationHe * fetches participation details from the local VC repository URI. * * @param session the {@link ServerSession} representing the SSH session. - * @param rootDir the {@link Path} to the root directory of the repository. * @param clientOffered the number of objects offered by the client in the operation, used to determine * if the action is a clone (if 0) or a pull (if greater than 0). */ - @Async - public void updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path rootDir, int clientOffered) { + public void updateAndStoreVCSAccessLogForCloneAndPullSSH(ServerSession session, int clientOffered) { try { if (session.getAttribute(SshConstants.USER_KEY).getName().equals(BUILD_USER_NAME)) { return; } + var accessLog = session.getAttribute(SshConstants.VCS_ACCESS_LOG_KEY); RepositoryActionType repositoryActionType = getRepositoryActionReadType(clientOffered); - var participation = retrieveParticipationFromLocalVCRepositoryUri(getLocalVCRepositoryUri(rootDir)); - vcsAccessLogService.ifPresent(service -> service.updateRepositoryActionType(participation, repositoryActionType)); + accessLog.setRepositoryActionType(repositoryActionType); + vcsAccessLogService.ifPresent(service -> service.saveVcsAccesslog(accessLog)); } catch (Exception ignored) { } @@ -854,7 +950,7 @@ public void updateVCSAccessLogForCloneAndPullSSH(ServerSession session, Path roo * Adds a failed VCS access attempt to the log. *

* This method logs a failed clone attempt, associating it with the user and participation retrieved - * from the incoming HTTP request. It assumes that the failed attempt used password authentication. + * from the incoming HTTP request. * * @param servletRequest the {@link HttpServletRequest} containing the HTTP request data. */ @@ -864,11 +960,14 @@ public void createVCSAccessLogForFailedAuthenticationAttempt(HttpServletRequest UsernameAndPassword usernameAndPassword = extractUsernameAndPassword(authorizationHeader); User user = userRepository.findOneByLogin(usernameAndPassword.username()).orElseThrow(LocalVCAuthException::new); AuthenticationMechanism mechanism = usernameAndPassword.password().startsWith("vcpat-") ? AuthenticationMechanism.VCS_ACCESS_TOKEN : AuthenticationMechanism.PASSWORD; - var participation = getExerciseParticipationFromRequest(servletRequest); + LocalVCRepositoryUri localVCRepositoryUri = parseRepositoryUri(servletRequest); + var participation = programmingExerciseParticipationService.fetchParticipationWithSubmissionsByRepository(localVCRepositoryUri.getRepositoryTypeOrUserName(), + localVCRepositoryUri.toString(), null); var ipAddress = servletRequest.getRemoteAddr(); - vcsAccessLogService.ifPresent(service -> service.storeAccessLog(user, participation, RepositoryActionType.CLONE_FAIL, mechanism, "", ipAddress)); + vcsAccessLogService.ifPresent(service -> service.saveAccessLog(user, participation, RepositoryActionType.CLONE_FAIL, mechanism, "", ipAddress)); } - catch (LocalVCAuthException ignored) { + catch (LocalVCAuthException | EntityNotFoundException ignored) { + // Caught when: 1) no user, or 2) no participation was found. In both cases it does not make sense to write a log } } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java index d45bfda1c179..fbe1f6450384 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/SshGitLocationResolverService.java @@ -71,14 +71,16 @@ public Path resolveRootDirectory(String command, String[] args, ServerSession se // git-upload-pack means fetch (read operation), git-receive-pack means push (write operation) final var repositoryAction = gitCommand.equals("git-upload-pack") ? RepositoryActionType.READ : gitCommand.equals("git-receive-pack") ? RepositoryActionType.WRITE : null; final var user = session.getAttribute(SshConstants.USER_KEY); + session.setAttribute(SshConstants.REPOSITORY_EXERCISE_KEY, exercise); if (session.getAttribute(SshConstants.IS_BUILD_AGENT_KEY) && repositoryAction == RepositoryActionType.READ) { // We already checked for build agent authenticity } else { try { - localVCServletService.authorizeUser(repositoryTypeOrUserName, user, exercise, repositoryAction, AuthenticationMechanism.SSH, session.getClientAddress().toString(), - localVCRepositoryUri); + var participation = localVCServletService.authorizeUser(repositoryTypeOrUserName, user, exercise, repositoryAction, localVCRepositoryUri, true); + localVCServletService.cacheAttributesInSshSession(user, participation, repositoryAction, AuthenticationMechanism.SSH, session.getClientAddress().toString(), + localVCRepositoryUri, session); } catch (LocalVCForbiddenException e) { log.error("User {} does not have access to the repository {}", user.getLogin(), repositoryPath); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java index af1972ce2e1c..583ceeebadaa 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/VcsAccessLogService.java @@ -2,6 +2,8 @@ import static de.tum.cit.aet.artemis.core.config.Constants.PROFILE_LOCALVC; +import java.util.Optional; + import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.errors.GitAPIException; import org.slf4j.Logger; @@ -46,7 +48,7 @@ public class VcsAccessLogService { * @param ipAddress The ip address of the user accessing the repository */ @Async - public void storeAccessLog(User user, ProgrammingExerciseParticipation participation, RepositoryActionType actionType, AuthenticationMechanism authenticationMechanism, + public void saveAccessLog(User user, ProgrammingExerciseParticipation participation, RepositoryActionType actionType, AuthenticationMechanism authenticationMechanism, String commitHash, String ipAddress) { log.debug("Storing access operation for user {}", user); @@ -63,44 +65,60 @@ public void storeAccessLog(User user, ProgrammingExerciseParticipation participa */ @Async public void updateCommitHash(ProgrammingExerciseParticipation participation, String commitHash) { - vcsAccessLogRepository.findNewestByParticipationId(participation.getId()).ifPresent(entry -> { - entry.setCommitHash(commitHash); - vcsAccessLogRepository.save(entry); - }); + var vcsAccessLog = vcsAccessLogRepository.findNewestByParticipationId(participation.getId()); + if (vcsAccessLog.isPresent()) { + vcsAccessLog.get().setCommitHash(commitHash); + vcsAccessLogRepository.save(vcsAccessLog.get()); + } + } + + /** + * Updates the commit hash of the newest log entry + * + * @param localVCRepositoryUri The localVCRepositoryUri of the participation to which vcsAccessLog belongs to + * @param repositoryActionType The repository action type to which the vcsAccessLog should get updated to + */ + @Async + public void updateRepositoryActionType(LocalVCRepositoryUri localVCRepositoryUri, RepositoryActionType repositoryActionType) { + var repositoryURL = localVCRepositoryUri.toString().replace("/git-upload-pack", "").replace("/git-receive-pack", ""); + var vcsAccessLog = vcsAccessLogRepository.findNewestByRepositoryUri(repositoryURL); + if (vcsAccessLog.isPresent()) { + vcsAccessLog.get().setRepositoryActionType(repositoryActionType); + vcsAccessLogRepository.save(vcsAccessLog.get()); + } } /** - * Updates the repository action type of the newest log entry. This method is not Async, as it should already be called from an @Async context + * Saves an vcsAccessLog * - * @param participation The participation to which the repository belongs to - * @param repositoryActionType The repositoryActionType which should get set for the newest access log entry + * @param vcsAccessLog The vcsAccessLog to save */ - public void updateRepositoryActionType(ProgrammingExerciseParticipation participation, RepositoryActionType repositoryActionType) { - vcsAccessLogRepository.findNewestByParticipationId(participation.getId()).ifPresent(entry -> { - entry.setRepositoryActionType(repositoryActionType); - vcsAccessLogRepository.save(entry); - }); + @Async + public void saveVcsAccesslog(VcsAccessLog vcsAccessLog) { + vcsAccessLogRepository.save(vcsAccessLog); } /** - * Stores the log for a push from the code editor. + * Creates a preliminary access log for a push from the code editor, and returns it * * @param repo The repository to which the push is executed * @param user The user submitting the change * @param participationId The id of the participation belonging to the repository + * @return an Optional containing the preliminary VcsAccessLog, if one was created * @throws GitAPIException if an error occurs while retrieving the git log */ - public void storeCodeEditorAccessLog(Repository repo, User user, Long participationId) throws GitAPIException { + public Optional createPreliminaryCodeEditorAccessLog(Repository repo, User user, Long participationId) throws GitAPIException { try (Git git = new Git(repo)) { String lastCommitHash = git.log().setMaxCount(1).call().iterator().next().getName(); var participation = participationRepository.findById(participationId); if (participation.isPresent() && participation.get() instanceof ProgrammingExerciseParticipation programmingParticipation) { log.debug("Storing access operation for user {}", user); - VcsAccessLog accessLogEntry = new VcsAccessLog(user, (Participation) programmingParticipation, user.getName(), user.getEmail(), RepositoryActionType.WRITE, - AuthenticationMechanism.CODE_EDITOR, lastCommitHash, null); - vcsAccessLogRepository.save(accessLogEntry); + return Optional.of(new VcsAccessLog(user, (Participation) programmingParticipation, user.getName(), user.getEmail(), RepositoryActionType.WRITE, + AuthenticationMechanism.CODE_EDITOR, lastCommitHash, null)); } } + return Optional.empty(); } + } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshConstants.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshConstants.java index 1ea41e9d2d6e..f7a0f02258ed 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshConstants.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshConstants.java @@ -6,6 +6,9 @@ import org.springframework.context.annotation.Profile; import de.tum.cit.aet.artemis.core.domain.User; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; +import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation; +import de.tum.cit.aet.artemis.programming.domain.VcsAccessLog; @Profile(PROFILE_LOCALVC) public class SshConstants { @@ -13,4 +16,10 @@ public class SshConstants { public static final AttributeRepository.AttributeKey IS_BUILD_AGENT_KEY = new AttributeRepository.AttributeKey<>(); public static final AttributeRepository.AttributeKey USER_KEY = new AttributeRepository.AttributeKey<>(); + + public static final AttributeRepository.AttributeKey REPOSITORY_EXERCISE_KEY = new AttributeRepository.AttributeKey<>(); + + public static final AttributeRepository.AttributeKey VCS_ACCESS_LOG_KEY = new AttributeRepository.AttributeKey<>(); + + public static final AttributeRepository.AttributeKey PARTICIPATION_KEY = new AttributeRepository.AttributeKey<>(); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshGitCommand.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshGitCommand.java index 7ec968646217..e078bce7fdbe 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshGitCommand.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/ssh/SshGitCommand.java @@ -85,13 +85,13 @@ public void run() { if (GenericUtils.isNotBlank(protocol)) { uploadPack.setExtraParameters(Collections.singleton(protocol)); } - uploadPack.setPreUploadHook(new LocalVCFetchPreUploadHookSSH(localVCServletService, getServerSession(), rootDir)); + uploadPack.setPreUploadHook(new LocalVCFetchPreUploadHookSSH(localVCServletService, getServerSession())); uploadPack.upload(getInputStream(), getOutputStream(), getErrorStream()); } else if (RemoteConfig.DEFAULT_RECEIVE_PACK.equals(subCommand)) { var receivePack = new ReceivePack(repository); receivePack.setPreReceiveHook(new LocalVCPrePushHook(localVCServletService, user)); - receivePack.setPostReceiveHook(new LocalVCPostPushHook(localVCServletService)); + receivePack.setPostReceiveHook(new LocalVCPostPushHook(localVCServletService, getServerSession())); receivePack.receive(getInputStream(), getOutputStream(), getErrorStream()); } else { diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryResource.java b/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryResource.java index f9ca7350afd1..abe9b9ec40e2 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryResource.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/web/repository/RepositoryResource.java @@ -281,12 +281,14 @@ public ResponseEntity commitChanges(Long domainId) { return executeAndCheckForExceptions(() -> { Repository repository = getRepository(domainId, RepositoryActionType.WRITE, true); - repositoryService.commitChanges(repository, user, domainId); + repositoryService.commitChanges(repository, user); + var vcsAccessLog = repositoryService.savePreliminaryCodeEditorAccessLog(repository, user, domainId); + // Trigger a build, and process the result. Only implemented for local CI. // For GitLab + Jenkins, webhooks were added when creating the repository, // that notify the CI system when the commit happens and thus trigger the build. if (profileService.isLocalVcsCiActive()) { - localVCServletService.orElseThrow().processNewPush(null, repository); + localVCServletService.orElseThrow().processNewPush(null, repository, Optional.empty(), Optional.empty(), vcsAccessLog); } return new ResponseEntity<>(HttpStatus.OK); }); diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/TemplateProgrammingExerciseParticipationTestRepository.java b/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/TemplateProgrammingExerciseParticipationTestRepository.java new file mode 100644 index 000000000000..4eb0d9f8c22b --- /dev/null +++ b/src/test/java/de/tum/cit/aet/artemis/programming/test_repository/TemplateProgrammingExerciseParticipationTestRepository.java @@ -0,0 +1,24 @@ +package de.tum.cit.aet.artemis.programming.test_repository; + +import static org.springframework.data.jpa.repository.EntityGraph.EntityGraphType.LOAD; + +import java.util.Optional; + +import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.stereotype.Repository; + +import de.tum.cit.aet.artemis.programming.domain.TemplateProgrammingExerciseParticipation; +import de.tum.cit.aet.artemis.programming.repository.TemplateProgrammingExerciseParticipationRepository; + +@Repository +@Primary +public interface TemplateProgrammingExerciseParticipationTestRepository extends TemplateProgrammingExerciseParticipationRepository { + + @EntityGraph(type = LOAD, attributePaths = { "results", "submissions" }) + Optional findWithEagerResultsAndSubmissionsByProgrammingExerciseId(long exerciseId); + + default TemplateProgrammingExerciseParticipation findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(long exerciseId) { + return getValueElseThrow(findWithEagerResultsAndSubmissionsByProgrammingExerciseId(exerciseId)); + } +} diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java index 2c4321d22b79..d2a64d1a0ddd 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingExerciseUtilService.java @@ -67,12 +67,12 @@ import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; import de.tum.cit.aet.artemis.programming.repository.StaticCodeAnalysisCategoryRepository; import de.tum.cit.aet.artemis.programming.repository.SubmissionPolicyRepository; -import de.tum.cit.aet.artemis.programming.repository.TemplateProgrammingExerciseParticipationRepository; import de.tum.cit.aet.artemis.programming.service.GitService; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTaskTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestCaseTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.TemplateProgrammingExerciseParticipationTestRepository; /** * Service responsible for initializing the database with specific testdata related to programming exercises for use in integration tests. @@ -91,7 +91,7 @@ public class ProgrammingExerciseUtilService { protected String artemisVersionControlUrl; @Autowired - private TemplateProgrammingExerciseParticipationRepository templateProgrammingExerciseParticipationRepo; + private TemplateProgrammingExerciseParticipationTestRepository templateProgrammingExerciseParticipationTestRepo; @Autowired private ProgrammingExerciseTestRepository programmingExerciseRepository; @@ -191,7 +191,7 @@ public ProgrammingExercise addTemplateParticipationForProgrammingExercise(Progra participation.setBuildPlanId(exercise.generateBuildPlanId(BuildPlanType.TEMPLATE)); participation.setRepositoryUri(String.format("%s/git/%s/%s.git", artemisVersionControlUrl, exercise.getProjectKey(), repoName)); participation.setInitializationState(InitializationState.INITIALIZED); - templateProgrammingExerciseParticipationRepo.save(participation); + templateProgrammingExerciseParticipationTestRepo.save(participation); exercise.setTemplateParticipation(participation); return programmingExerciseRepository.save(exercise); } @@ -745,7 +745,7 @@ public Result addProgrammingSubmissionWithResult(ProgrammingExercise exercise, P * @return the newly created result */ public Result addTemplateSubmissionWithResult(long exerciseId) { - var templateParticipation = templateProgrammingExerciseParticipationRepo.findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(exerciseId); + var templateParticipation = templateProgrammingExerciseParticipationTestRepo.findWithEagerResultsAndSubmissionsByProgrammingExerciseIdElseThrow(exerciseId); ProgrammingSubmission submission = new ProgrammingSubmission(); submission = submissionRepository.save(submission); Result result = resultRepo.save(new Result().participation(templateParticipation)); @@ -756,7 +756,7 @@ public Result addTemplateSubmissionWithResult(long exerciseId) { result.setSubmission(submission); result = resultRepo.save(result); templateParticipation.addResult(result); - templateProgrammingExerciseParticipationRepo.save(templateParticipation); + templateProgrammingExerciseParticipationTestRepo.save(templateParticipation); return result; } diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingUtilTestService.java b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingUtilTestService.java index 52a58e3f8040..78f7f370e454 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingUtilTestService.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/util/ProgrammingUtilTestService.java @@ -27,11 +27,11 @@ import de.tum.cit.aet.artemis.programming.domain.Repository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.TemplateProgrammingExerciseParticipationRepository; import de.tum.cit.aet.artemis.programming.service.GitService; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingSubmissionTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.TemplateProgrammingExerciseParticipationTestRepository; /** * Utility service specifically used for testing programming exercises. @@ -55,7 +55,7 @@ public class ProgrammingUtilTestService { private ProgrammingSubmissionTestRepository programmingSubmissionRepository; @Autowired - private TemplateProgrammingExerciseParticipationRepository templateProgrammingExerciseParticipationRepository; + private TemplateProgrammingExerciseParticipationTestRepository templateProgrammingExerciseParticipationRepository; @Autowired private SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseParticipationRepository; diff --git a/src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java b/src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java index 90e651dfbac8..e123235d449e 100644 --- a/src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/shared/base/AbstractSpringIntegrationLocalCILocalVCTest.java @@ -87,13 +87,13 @@ import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository; import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildStatisticsRepository; import de.tum.cit.aet.artemis.programming.repository.SolutionProgrammingExerciseParticipationRepository; -import de.tum.cit.aet.artemis.programming.repository.TemplateProgrammingExerciseParticipationRepository; import de.tum.cit.aet.artemis.programming.service.ProgrammingMessagingService; import de.tum.cit.aet.artemis.programming.service.localci.LocalCIService; import de.tum.cit.aet.artemis.programming.service.localvc.LocalVCService; import de.tum.cit.aet.artemis.programming.test_repository.BuildJobTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseStudentParticipationTestRepository; import de.tum.cit.aet.artemis.programming.test_repository.ProgrammingExerciseTestRepository; +import de.tum.cit.aet.artemis.programming.test_repository.TemplateProgrammingExerciseParticipationTestRepository; // Must start up an actual web server such that the tests can communicate with the ArtemisGitServlet using JGit. // Otherwise, only MockMvc requests could be used. The port this runs on is defined at server.port (see @TestPropertySource). @@ -130,7 +130,7 @@ public abstract class AbstractSpringIntegrationLocalCILocalVCTest extends Abstra protected ProgrammingExerciseBuildStatisticsRepository programmingExerciseBuildStatisticsRepository; @Autowired - protected TemplateProgrammingExerciseParticipationRepository templateProgrammingExerciseParticipationRepository; + protected TemplateProgrammingExerciseParticipationTestRepository templateProgrammingExerciseParticipationRepository; @Autowired protected SolutionProgrammingExerciseParticipationRepository solutionProgrammingExerciseParticipationRepository; From 031c0bf3384970eceebdd9dfcd2be4e4245a6d58 Mon Sep 17 00:00:00 2001 From: Stephan Krusche Date: Sun, 12 Jan 2025 11:37:28 +0100 Subject: [PATCH 06/16] Development: Update thymeleaf email templates (#10114) --- README.md | 6 +- docs/admin/registration.rst | 2 +- docs/user/mobile-applications.rst | 4 +- docs/user/orion.rst | 4 +- .../service/localvc/LocalVCRepositoryUri.java | 16 +- .../mail/dataExportFailedAdminEmail.html | 6 +- .../notification/announcementPostEmail.html | 14 +- .../notification/attachmentChangedEmail.html | 14 +- .../notification/dataExportCreatedEmail.html | 12 +- .../notification/dataExportFailedEmail.html | 10 +- .../notification/duplicateTestCasesEmail.html | 14 +- .../exerciseOpenForPracticeEmail.html | 16 +- .../notification/exerciseReleasedEmail.html | 16 +- .../exerciseSubmissionAssessedEmail.html | 12 +- .../fileSubmissionSuccessfulEmail.html | 12 +- .../mail/notification/fragments.html | 317 ++++++++---------- .../notification/plagiarismCaseEmail.html | 6 +- .../notification/plagiarismVerdictEmail.html | 14 +- .../notification/tutorialGroupBasicEmail.html | 12 +- .../tutorialGroupDeletedEmail.html | 10 +- .../tutorialGroupUpdatedEmail.html | 14 +- .../mail/successfulDataExportsAdminEmail.html | 6 +- .../templates/mail/weeklySummary.html | 12 +- .../app/core/about-us/about-us.component.ts | 2 +- .../programming/icl/RepositoryUriTest.java | 26 +- .../service/external-cloning.service.spec.ts | 6 +- 26 files changed, 271 insertions(+), 312 deletions(-) diff --git a/README.md b/README.md index 99248761d635..d2dc95b1d5e9 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Artemis brings interactive learning to life with instant, individual feedback on * **Integrated feedback**: Reviews can provide feedback and points directly next to the text segments. * **Language detection**: Artemis detects the language of the submission and shows the word and character count. 7. **[File upload exercises](https://docs.artemis.cit.tum.de/user/exercises/file-upload/)** allow full flexibility to instructors. Students can create any kind of file (e.g. PDF, PNG) and submit it to Artemis when they have completed their work. Artemis allows instructors and tutors to download the files and assess them manually based on structured grading criteria (see below in the section Assessment). -8. **[Exam mode](https://docs.artemis.cit.tum.de/user/exam_mode/)**: Instructors can create online exams with exercise variants, integrated plagiarism checks, test runs and student reviews. You can find more information on [Exam mode student features](https://artemis.cit.tum.de/features/students) and on [Exam mode instructor features](https://artemis.cit.tum.de/features/instructors). +8. **[Exam mode](https://docs.artemis.cit.tum.de/user/exam_mode/)**: Instructors can create online exams with exercise variants, integrated plagiarism checks, test runs and student reviews. You can find more information on [Exam mode student features](https://artemis.tum.de/features/students) and on [Exam mode instructor features](https://artemis.tum.de/features/instructors). 9. **[Assessment](https://docs.artemis.cit.tum.de/user/exercises/assessment/)**: Artemis uses double-blind grading and structured grading criteria to improve consistency and fairness. * **Training process**: It integrates an assessment training process (based on example submissions and example assessments defined by the instructor), has a grading leader board, and allows students to rate the assessments. Students can complain or ask for more feedback. * **Grading**: Instructors can configure grade keys for courses and exams to automatically calculate grades and display them to students. Grades can be easily exported as csv files to upload them into university systems (such as Campus online). They can define bonus configurations for final exams to improve student grades according to their grades from a midterm exam or course exercises. @@ -59,7 +59,7 @@ Artemis brings interactive learning to life with instant, individual feedback on * **[Adaptive learning](https://docs.artemis.cit.tum.de/user/adaptive-learning)**: Artemis allows instructors and students to define and track competencies. Students can monitor their progress towards these goals, while instructors can provide tailored feedback. This approach integrates lectures and exercises under overarching learning objectives. * **[Learning analytics](https://docs.artemis.cit.tum.de/user/learning-analytics)**: Artemis integrated different statistics for students to compare themselves to the course average. It allows instructors to evaluate the average student performance based on exercises and competencies. * **[Learning paths](https://docs.artemis.cit.tum.de/user/adaptive-learning/adaptive-learning-student.html#learning-paths)**: Based on the competency model and students' individual progress, Artemis creates learning paths that guide students through the course content. -13. **[Iris](https://artemis.cit.tum.de/about-iris)**: Artemis integrates Iris, a LLM based virtual assistant that supports students and instructors with common questions and tasks. +13. **[Iris](https://artemis.tum.de/about-iris)**: Artemis integrates Iris, a LLM based virtual assistant that supports students and instructors with common questions and tasks. * **Questions**: Iris supports students with answering questions about exercises, lectures, and the learning performance instantly. * **Pro-active assistance**: Iris can pro-actively communicate with the students, help them with the next steps in their learning experience and motivate them to continue. 14. **[Athena](https://github.com/ls1intum/Athena)**: Artemis integrates Athena, a machine learning-based tool that supports instructors with the assessment of text, modeling and programming exercises. Athena offers different modules including automatic feedback suggestions based on generate AI. @@ -242,7 +242,7 @@ We communicate using GitHub issues and pull requests. Additionally, you can join The following universities are actively using Artemis or are currently evaluating Artemis. * **Technical University of Munich** - https://artemis.cit.tum.de + https://artemis.tum.de Main contact person: [Stephan Krusche](mailto:krusche@tum.de) * **LFU Innsbruck, Uni Salzburg, JKU Linz, AAU Klagenfurt, TU Wien** diff --git a/docs/admin/registration.rst b/docs/admin/registration.rst index bb16b33cd78c..ad583a52cfa3 100644 --- a/docs/admin/registration.rst +++ b/docs/admin/registration.rst @@ -30,7 +30,7 @@ Example: trust: jhipster: mail: - base-url: https://artemis.cit.tum.de + base-url: https://artemis.tum.de from: artemis@xcit.tum.de management: health: diff --git a/docs/user/mobile-applications.rst b/docs/user/mobile-applications.rst index 64485cb2e2fd..15732acfe684 100644 --- a/docs/user/mobile-applications.rst +++ b/docs/user/mobile-applications.rst @@ -59,7 +59,7 @@ Server Selection After installation, users have to first decide which Artemis server they want to connect to. Per default, the user can choose between the following instances: -* TUM: https://artemis.cit.tum.de +* TUM: https://artemis.tum.de * CodeAbility: https://artemis.codeability.uibk.ac.at * KIT: https://artemis.praktomat.cs.kit.edu @@ -170,7 +170,7 @@ Server Selection After installation, users have to first decide which Artemis server they want to connect to. Per default, the user can choose between the following instances: -* TUM: https://artemis.cit.tum.de +* TUM: https://artemis.tum.de * CodeAbility: https://artemis.codeability.uibk.ac.at * KIT: https://artemis.praktomat.cs.kit.edu * Hochschule Munich: https://artemis.cs.hm.edu/ diff --git a/docs/user/orion.rst b/docs/user/orion.rst index d6abba219fc6..1a7d857f7c07 100644 --- a/docs/user/orion.rst +++ b/docs/user/orion.rst @@ -39,8 +39,8 @@ Settings Orion's settings are at *Settings -> Tools -> Orion*. The settings include: -- Artemis base url: Can be changed to switch to a specific Artemis instance. Defaults to https://artemis.cit.tum.de. **Important:** The url must not end with a ``/``, otherwise it does not work! -- Artemis git url: Can be changed to switch to a specific Artemis instance. Defaults to https://artemis.cit.tum.de/git +- Artemis base url: Can be changed to switch to a specific Artemis instance. Defaults to https://artemis.tum.de. **Important:** The url must not end with a ``/``, otherwise it does not work! +- Artemis git url: Can be changed to switch to a specific Artemis instance. Defaults to https://artemis.tum.de/git - Artemis exercise paths: Orion suggests to store newly downloaded exercises at ``default-path/course/exercise-name``, with the default path dependent of the setting. - Default commit message: The default message for each commit. - Change user agent: The user agent is sent to Artemis to identify Orion. Usually, no changes are required. diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCRepositoryUri.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCRepositoryUri.java index 796c0df5fc02..8103863650fa 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCRepositoryUri.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/localvc/LocalVCRepositoryUri.java @@ -101,10 +101,10 @@ public LocalVCRepositoryUri(String urlString) { * * Examples: *

    - *
  • Correct URL: "https://artemis.cit.tum.de/git/projectKey/repositorySlug.git" - Returns 1 as "git" is at index 1.
  • - *
  • Incorrect URL: "https://artemis.cit.tum.de/projectKey/repositorySlug.git" - Throws LocalVCInternalException because the "git" segment is missing.
  • - *
  • Incorrect URL: "https://artemis.cit.tum.de/git/projectKey" - Throws LocalVCInternalException because there are not enough segments after "git".
  • - *
  • Incorrect URL: "https://artemis.cit.tum.de/git/projectKey/repositorySlug" - Throws LocalVCInternalException because the repository slug does not end with ".git".
  • + *
  • Correct URL: "https://artemis.tum.de/git/projectKey/repositorySlug.git" - Returns 1 as "git" is at index 1.
  • + *
  • Incorrect URL: "https://artemis.tum.de/projectKey/repositorySlug.git" - Throws LocalVCInternalException because the "git" segment is missing.
  • + *
  • Incorrect URL: "https://artemis.tum.de/git/projectKey" - Throws LocalVCInternalException because there are not enough segments after "git".
  • + *
  • Incorrect URL: "https://artemis.tum.de/git/projectKey/repositorySlug" - Throws LocalVCInternalException because the repository slug does not end with ".git".
  • *
* * @param urlString The full URL string being analyzed, provided for context in error messages. @@ -148,13 +148,13 @@ private static int getGitPartStartIndex(String urlString, Path urlPath) throws L *
    *
  • * Input: Local repository path - {@code Paths.get("/local/path/projectX/my-repo/.git")} - * and Local VC server URL - {@code new URI("https://artemis.cit.tum.de").getURL()} - * Output: {@code https://artemis.cit.tum.de/git/projectX/my-repo.git} + * and Local VC server URL - {@code new URI("https://artemis.tum.de").getURL()} + * Output: {@code https://artemis.tum.de/git/projectX/my-repo.git} *
  • *
  • * Input: Remote repository path - {@code Paths.get("/remote/path/projectY/my-repo")} - * and Local VC server URL - {@code new URI("https://artemis.cit.tum.de").getURL()} - * Output: {@code https://artemis.cit.tum.de/git/projectY/my-repo.git} + * and Local VC server URL - {@code new URI("https://artemis.tum.de").getURL()} + * Output: {@code https://artemis.tum.de/git/projectY/my-repo.git} *
  • *
* diff --git a/src/main/resources/templates/mail/dataExportFailedAdminEmail.html b/src/main/resources/templates/mail/dataExportFailedAdminEmail.html index 14d68464eac7..ad7141a76b1e 100644 --- a/src/main/resources/templates/mail/dataExportFailedAdminEmail.html +++ b/src/main/resources/templates/mail/dataExportFailedAdminEmail.html @@ -2,11 +2,11 @@ Data export for one of the users failed - - + + - +
A data export creation failed. diff --git a/src/main/resources/templates/mail/notification/announcementPostEmail.html b/src/main/resources/templates/mail/notification/announcementPostEmail.html index 6dbd0eb48d1b..391fb5567e88 100644 --- a/src/main/resources/templates/mail/notification/announcementPostEmail.html +++ b/src/main/resources/templates/mail/notification/announcementPostEmail.html @@ -1,25 +1,25 @@ - + - +
- +
Notification Content for Announcement Posts - +

Post Content

- +
- +
- + diff --git a/src/main/resources/templates/mail/notification/attachmentChangedEmail.html b/src/main/resources/templates/mail/notification/attachmentChangedEmail.html index 769bb3f1491c..c4a080fc4c24 100644 --- a/src/main/resources/templates/mail/notification/attachmentChangedEmail.html +++ b/src/main/resources/templates/mail/notification/attachmentChangedEmail.html @@ -1,11 +1,11 @@ - + - +
- +
Notification Content for Attachment Changed - - + +
- +
- + diff --git a/src/main/resources/templates/mail/notification/dataExportCreatedEmail.html b/src/main/resources/templates/mail/notification/dataExportCreatedEmail.html index 7f4075a08e75..398e593c15fa 100644 --- a/src/main/resources/templates/mail/notification/dataExportCreatedEmail.html +++ b/src/main/resources/templates/mail/notification/dataExportCreatedEmail.html @@ -1,16 +1,16 @@ - + - +
- + Your data export has been successfully created - - + +
- + diff --git a/src/main/resources/templates/mail/notification/dataExportFailedEmail.html b/src/main/resources/templates/mail/notification/dataExportFailedEmail.html index d7dc4c5ab6f0..cff8d2814dd5 100644 --- a/src/main/resources/templates/mail/notification/dataExportFailedEmail.html +++ b/src/main/resources/templates/mail/notification/dataExportFailedEmail.html @@ -1,15 +1,15 @@ - + - +
- + Your data export creation failed. - +
- + diff --git a/src/main/resources/templates/mail/notification/duplicateTestCasesEmail.html b/src/main/resources/templates/mail/notification/duplicateTestCasesEmail.html index 31a0d188997f..e0cbb079fc03 100644 --- a/src/main/resources/templates/mail/notification/duplicateTestCasesEmail.html +++ b/src/main/resources/templates/mail/notification/duplicateTestCasesEmail.html @@ -1,13 +1,13 @@ - + - +
- +
- + Due Date - +
- +
- + diff --git a/src/main/resources/templates/mail/notification/exerciseOpenForPracticeEmail.html b/src/main/resources/templates/mail/notification/exerciseOpenForPracticeEmail.html index ca0b83a2a97f..6097451cf482 100644 --- a/src/main/resources/templates/mail/notification/exerciseOpenForPracticeEmail.html +++ b/src/main/resources/templates/mail/notification/exerciseOpenForPracticeEmail.html @@ -1,13 +1,13 @@ - + - +
- +
- +

Information about this exercise:

    -
  • +
- +
- +
- + diff --git a/src/main/resources/templates/mail/notification/exerciseReleasedEmail.html b/src/main/resources/templates/mail/notification/exerciseReleasedEmail.html index 486ad02c4e2b..3c819c83e055 100644 --- a/src/main/resources/templates/mail/notification/exerciseReleasedEmail.html +++ b/src/main/resources/templates/mail/notification/exerciseReleasedEmail.html @@ -1,13 +1,13 @@ - + - +
- +
- + Information about this exercise:

    -
  • +
  • Release Date
  • @@ -28,11 +28,11 @@
  • Bonus Points
- +
- +
- + diff --git a/src/main/resources/templates/mail/notification/exerciseSubmissionAssessedEmail.html b/src/main/resources/templates/mail/notification/exerciseSubmissionAssessedEmail.html index 9758a0bb6208..48543300e523 100644 --- a/src/main/resources/templates/mail/notification/exerciseSubmissionAssessedEmail.html +++ b/src/main/resources/templates/mail/notification/exerciseSubmissionAssessedEmail.html @@ -1,11 +1,11 @@ - + - +
- +
Max Points
  • Score
  • - +
    - +
    - + diff --git a/src/main/resources/templates/mail/notification/fileSubmissionSuccessfulEmail.html b/src/main/resources/templates/mail/notification/fileSubmissionSuccessfulEmail.html index 4af88f5f6340..1b47c689b4da 100644 --- a/src/main/resources/templates/mail/notification/fileSubmissionSuccessfulEmail.html +++ b/src/main/resources/templates/mail/notification/fileSubmissionSuccessfulEmail.html @@ -1,11 +1,11 @@ - + - +
    - +
    Exercise Due Date - +
    - +
    - + diff --git a/src/main/resources/templates/mail/notification/fragments.html b/src/main/resources/templates/mail/notification/fragments.html index 266a4291c74b..a8848b157546 100644 --- a/src/main/resources/templates/mail/notification/fragments.html +++ b/src/main/resources/templates/mail/notification/fragments.html @@ -1,178 +1,148 @@ - + Notification Title - - + + - + + /* CSS styles here */ + body { + font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 19px; + margin: 0 auto; + background: white; + color: black; + } + a { + color: #3e8acc; + } + pre { + padding: 10px 24px; + max-width: 800px; + white-space: pre-wrap; + background-color: #47474b; + color: white; + border-radius: 3px; + } + code { + font-family: Consolas, Monaco, Andale Mono, monospace; + line-height: 1.5; + font-size: 13px; + } + p code { + background-color: #47474b; + color: white; + border-radius: 3px; + padding: 3px 5px; + } + blockquote { + margin: 1em; + max-width: 476px; + border-left: 5px solid gray; + padding-left: 12px; + } + blockquote p { + color: #666; + max-width: 460px; + } + .notification-text-header { + padding-top: 10px; + } + .notification-text { + font-style: italic; + font-weight: bold; + padding-top: 3px; + } + ul { + padding-left: 20px; + } + .button { + background-color: #5B9CD4; + color: white !important; /* Without important, color was overridden in Gmail */ + padding: 8px 12px ; + border-radius: 8px; + text-decoration: none; + } + .button-wrapper { + padding: 25px 25px 25px 0; + } + .emergency-link { + color: gray; + font-size: small; + } + .emergency-link > * { + margin: 0; + } + .notification-content { + margin-top: 10px; + } + .notification-content p { + margin-top: 0; + } + .notification-content p:first-child:empty, .notification-content p:last-child:empty { + display: none; + } + .bordered-content { + border-radius: 3px; + border: 1px solid #ccc; + background: #f7f7f7; + box-shadow: rgba(0, 0, 0, 0.24) 0 3px 8px; + margin: 15px 0; + padding: 10px; + } + .bold-text { + font-weight: bold; + } + header { + background-color: #353D47; + padding: 10px; + color: white; + } + #header-table { + border: none; + } + #logo { + max-width: 50px; + vertical-align: middle; + } + #app-name { + padding-left: 10px; + } + #message-body { + background-color: white; + padding: 10px; + } + footer { + background-color: #353D47; + color: white; + font-size: smaller; + padding: 5px; + } + - + - - +
    } diff --git a/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.ts b/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.ts index 89dc3774bf64..d8b5a79c7933 100644 --- a/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.ts +++ b/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.ts @@ -2,23 +2,30 @@ import { Component, Input, OnChanges } from '@angular/core'; import { StudentExam } from 'app/entities/student-exam.model'; import { Exam } from 'app/entities/exam/exam.model'; import { endTime, examWorkingTime, getAdditionalWorkingTime, isExamOverMultipleDays } from 'app/exam/participate/exam.utils'; +import { StudentExamWorkingTimeComponent } from 'app/exam/shared/student-exam-working-time/student-exam-working-time.component'; +import { TestExamWorkingTimeComponent } from 'app/exam/shared/testExam-workingTime/test-exam-working-time.component'; import dayjs from 'dayjs/esm'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; @Component({ selector: 'jhi-exam-general-information', styleUrls: ['./exam-general-information.component.scss'], templateUrl: './exam-general-information.component.html', + imports: [TranslateDirective, StudentExamWorkingTimeComponent, TestExamWorkingTimeComponent, ArtemisDatePipe, ArtemisTranslatePipe, ArtemisDurationFromSecondsPipe], }) export class ExamGeneralInformationComponent implements OnChanges { @Input() exam: Exam; @Input() studentExam: StudentExam; - @Input() reviewIsOpen?: boolean = false; + @Input() reviewIsOpen = false; /** * The exam cover will contain e.g. the number of exercises which is hidden in the exam summary as * the information is shown in the {@link ExamResultOverviewComponent} */ - @Input() displayOnExamCover?: boolean = false; + @Input() displayOnExamCover = false; examEndDate?: dayjs.Dayjs; normalWorkingTime?: number; @@ -27,7 +34,7 @@ export class ExamGeneralInformationComponent implements OnChanges { isTestExam?: boolean; currentDate?: dayjs.Dayjs; - ngOnChanges(): void { + ngOnChanges() { this.examEndDate = endTime(this.exam, this.studentExam); this.normalWorkingTime = examWorkingTime(this.exam); this.additionalWorkingTime = getAdditionalWorkingTime(this.exam, this.studentExam); diff --git a/src/main/webapp/app/exam/participate/summary/collapsible-card.component.ts b/src/main/webapp/app/exam/participate/summary/collapsible-card.component.ts index 8fd6833375fe..6d1eeceb1933 100644 --- a/src/main/webapp/app/exam/participate/summary/collapsible-card.component.ts +++ b/src/main/webapp/app/exam/participate/summary/collapsible-card.component.ts @@ -1,10 +1,13 @@ import { Component, Input } from '@angular/core'; import { faAngleRight } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'jhi-collapsible-card', templateUrl: './collapsible-card.component.html', styleUrls: ['../../../course/manage/course-exercise-card.component.scss', '../../../exercises/quiz/shared/quiz.scss', 'exam-result-summary.component.scss'], + imports: [FaIconComponent, NgbCollapse], }) export class CollapsibleCardComponent { @Input() isCardContentCollapsed: boolean; diff --git a/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.html b/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.html index 44eea193c092..dbf401451af5 100644 --- a/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.html +++ b/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.html @@ -19,7 +19,7 @@

    } diff --git a/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.ts b/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.ts index dc3af180c411..3f2988b91922 100644 --- a/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exam-result-summary.component.ts @@ -1,8 +1,8 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { StudentExam } from 'app/entities/student-exam.model'; import { Exercise, ExerciseType, IncludedInOverallScore, getIcon } from 'app/entities/exercise.model'; import dayjs from 'dayjs/esm'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { Exam } from 'app/entities/exam/exam.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; @@ -25,6 +25,23 @@ import { AlertService } from 'app/core/util/alert.service'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { isExamResultPublished } from 'app/exam/participate/exam.utils'; import { Course } from 'app/entities/course.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ExamGeneralInformationComponent } from '../general-information/exam-general-information.component'; +import { ExamResultOverviewComponent } from './result-overview/exam-result-overview.component'; +import { CollapsibleCardComponent } from './collapsible-card.component'; +import { ExamResultSummaryExerciseCardHeaderComponent } from './exercises/header/exam-result-summary-exercise-card-header.component'; +import { NgClass } from '@angular/common'; +import { ProgrammingExerciseExampleSolutionRepoDownloadComponent } from 'app/exercises/programming/shared/actions/programming-exercise-example-solution-repo-download.component'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ExampleSolutionComponent } from 'app/exercises/shared/example-solution/example-solution.component'; +import { TextExamSummaryComponent } from './exercises/text-exam-summary/text-exam-summary.component'; +import { ModelingExamSummaryComponent } from './exercises/modeling-exam-summary/modeling-exam-summary.component'; +import { QuizExamSummaryComponent } from './exercises/quiz-exam-summary/quiz-exam-summary.component'; +import { FileUploadExamSummaryComponent } from './exercises/file-upload-exam-summary/file-upload-exam-summary.component'; +import { ComplaintsStudentViewComponent } from 'app/complaints/complaints-for-students/complaints-student-view.component'; +import { ProgrammingExamSummaryComponent } from './exercises/programming-exam-summary/programming-exam-summary.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export type ResultSummaryExerciseInfo = { icon: IconProp; @@ -50,8 +67,35 @@ type StateBeforeResetting = { selector: 'jhi-exam-participation-summary', templateUrl: './exam-result-summary.component.html', styleUrls: ['../../../course/manage/course-exercise-card.component.scss', '../../../exercises/quiz/shared/quiz.scss', 'exam-result-summary.component.scss'], + imports: [ + FaIconComponent, + TranslateDirective, + ExamGeneralInformationComponent, + ExamResultOverviewComponent, + CollapsibleCardComponent, + ExamResultSummaryExerciseCardHeaderComponent, + NgClass, + RouterLink, + ProgrammingExerciseExampleSolutionRepoDownloadComponent, + NgbTooltip, + ExampleSolutionComponent, + TextExamSummaryComponent, + ModelingExamSummaryComponent, + QuizExamSummaryComponent, + FileUploadExamSummaryComponent, + ComplaintsStudentViewComponent, + ProgrammingExamSummaryComponent, + ArtemisTranslatePipe, + ], }) export class ExamResultSummaryComponent implements OnInit { + private route = inject(ActivatedRoute); + private serverDateService = inject(ArtemisServerDateService); + private themeService = inject(ThemeService); + private examParticipationService = inject(ExamParticipationService); + private plagiarismCasesService = inject(PlagiarismCasesService); + private alertService = inject(AlertService); + // make constants available to html for comparison readonly TEXT = ExerciseType.TEXT; readonly QUIZ = ExerciseType.QUIZ; @@ -95,8 +139,8 @@ export class ExamResultSummaryComponent implements OnInit { */ studentExamGradeInfoDTO: StudentExamWithGradeDTO; - isGradingKeyCollapsed: boolean = true; - isBonusGradingKeyCollapsed: boolean = true; + isGradingKeyCollapsed = true; + isBonusGradingKeyCollapsed = true; @Input() instructorView = false; @@ -132,15 +176,6 @@ export class ExamResultSummaryComponent implements OnInit { */ expandProblemStatement = false; - constructor( - private route: ActivatedRoute, - private serverDateService: ArtemisServerDateService, - private themeService: ThemeService, - private examParticipationService: ExamParticipationService, - private plagiarismCasesService: PlagiarismCasesService, - private alertService: AlertService, - ) {} - /** * Initialise the courseId from the current url */ diff --git a/src/main/webapp/app/exam/participate/summary/exam-result-summary.module.ts b/src/main/webapp/app/exam/participate/summary/exam-result-summary.module.ts index e3dada84b5e5..6ee8ac047277 100644 --- a/src/main/webapp/app/exam/participate/summary/exam-result-summary.module.ts +++ b/src/main/webapp/app/exam/participate/summary/exam-result-summary.module.ts @@ -57,8 +57,6 @@ import { NoDataComponent } from 'app/shared/no-data-component'; ArtemisFileUploadParticipationModule, ArtemisFeedbackModule, NoDataComponent, - ], - declarations: [ ExamResultSummaryComponent, ProgrammingExamSummaryComponent, ModelingExamSummaryComponent, diff --git a/src/main/webapp/app/exam/participate/summary/exercises/file-upload-exam-summary/file-upload-exam-summary.component.ts b/src/main/webapp/app/exam/participate/summary/exercises/file-upload-exam-summary/file-upload-exam-summary.component.ts index 0137462b144c..608f6e292c44 100644 --- a/src/main/webapp/app/exam/participate/summary/exercises/file-upload-exam-summary/file-upload-exam-summary.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exercises/file-upload-exam-summary/file-upload-exam-summary.component.ts @@ -1,17 +1,16 @@ import { Component, Input } from '@angular/core'; import { FileUploadSubmission } from 'app/entities/file-upload-submission.model'; import { Exercise } from 'app/entities/exercise.model'; +import { FileUploadSubmissionComponent } from 'app/exercises/file-upload/participate/file-upload-submission.component'; @Component({ selector: 'jhi-file-upload-exam-summary', templateUrl: './file-upload-exam-summary.component.html', + imports: [FileUploadSubmissionComponent], }) export class FileUploadExamSummaryComponent { @Input() submission: FileUploadSubmission; - @Input() exercise: Exercise; - - @Input() expandProblemStatement?: boolean = false; - - @Input() isAfterResultsArePublished?: boolean = false; + @Input() expandProblemStatement = false; + @Input() isAfterResultsArePublished = false; } diff --git a/src/main/webapp/app/exam/participate/summary/exercises/header/exam-result-summary-exercise-card-header.component.ts b/src/main/webapp/app/exam/participate/summary/exercises/header/exam-result-summary-exercise-card-header.component.ts index 9002b2cdfe30..185360ff0a15 100644 --- a/src/main/webapp/app/exam/participate/summary/exercises/header/exam-result-summary-exercise-card-header.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exercises/header/exam-result-summary-exercise-card-header.component.ts @@ -2,10 +2,15 @@ import { Component, Input } from '@angular/core'; import { Exercise } from 'app/entities/exercise.model'; import { ResultSummaryExerciseInfo } from 'app/exam/participate/summary/exam-result-summary.component'; import { SubmissionType } from 'app/entities/submission.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-result-summary-exercise-card-header', templateUrl: './exam-result-summary-exercise-card-header.component.html', + imports: [FaIconComponent, NgClass, TranslateDirective, ArtemisTranslatePipe], }) export class ExamResultSummaryExerciseCardHeaderComponent { @Input() index: number; diff --git a/src/main/webapp/app/exam/participate/summary/exercises/modeling-exam-summary/modeling-exam-summary.component.ts b/src/main/webapp/app/exam/participate/summary/exercises/modeling-exam-summary/modeling-exam-summary.component.ts index b8cafb1dda8b..e582e40860b1 100644 --- a/src/main/webapp/app/exam/participate/summary/exercises/modeling-exam-summary/modeling-exam-summary.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exercises/modeling-exam-summary/modeling-exam-summary.component.ts @@ -1,15 +1,17 @@ import { Component, Input } from '@angular/core'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; +import { ModelingSubmissionComponent } from 'app/exercises/modeling/participate/modeling-submission.component'; @Component({ selector: 'jhi-modeling-exam-summary', templateUrl: './modeling-exam-summary.component.html', + imports: [ModelingSubmissionComponent], }) export class ModelingExamSummaryComponent { @Input() exercise: ModelingExercise; @Input() submission: ModelingSubmission; - @Input() isPrinting?: boolean = false; - @Input() expandProblemStatement?: boolean = false; - @Input() isAfterResultsArePublished?: boolean = false; + @Input() isPrinting = false; + @Input() expandProblemStatement = false; + @Input() isAfterResultsArePublished = false; } diff --git a/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.ts b/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.ts index 5ffdffc38018..46a6c4e93cba 100644 --- a/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exercises/programming-exam-summary/programming-exam-summary.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit, Optional } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; @@ -12,34 +12,45 @@ import { ExerciseCacheService } from 'app/exercises/shared/exercise/exercise-cac import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { Result } from 'app/entities/result.model'; import { createCommitUrl } from 'app/exercises/programming/shared/utils/programming-exercise.utils'; -import { faCodeBranch } from '@fortawesome/free-solid-svg-icons'; import { Router } from '@angular/router'; import { PROFILE_LOCALVC } from 'app/app.constants'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CodeButtonComponent } from 'app/shared/components/code-button/code-button.component'; +import { FeedbackComponent } from 'app/exercises/shared/feedback/feedback.component'; +import { ProgrammingExerciseInstructionComponent } from 'app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component'; +import { ComplaintsStudentViewComponent } from 'app/complaints/complaints-for-students/complaints-student-view.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-programming-exam-summary', templateUrl: './programming-exam-summary.component.html', + imports: [TranslateDirective, CodeButtonComponent, FeedbackComponent, ProgrammingExerciseInstructionComponent, ComplaintsStudentViewComponent, ArtemisTranslatePipe], }) export class ProgrammingExamSummaryComponent implements OnInit { + private exerciseService = inject(ExerciseService); + private exerciseCacheService = inject(ExerciseCacheService, { optional: true }); + private profileService = inject(ProfileService); + private router = inject(Router); + @Input() exercise: ProgrammingExercise; @Input() participation: ProgrammingExerciseStudentParticipation; @Input() submission: ProgrammingSubmission; - @Input() isTestRun?: boolean = false; + @Input() isTestRun = false; @Input() exam: Exam; - @Input() isAfterStudentReviewStart?: boolean = false; + @Input() isAfterStudentReviewStart = false; - @Input() resultsPublished?: boolean = false; + @Input() resultsPublished = false; - @Input() isPrinting?: boolean = false; + @Input() isPrinting = false; - @Input() isAfterResultsArePublished?: boolean = false; + @Input() isAfterResultsArePublished = false; - @Input() instructorView?: boolean = false; + @Input() instructorView = false; readonly PROGRAMMING: ExerciseType = ExerciseType.PROGRAMMING; @@ -53,18 +64,10 @@ export class ProgrammingExamSummaryComponent implements OnInit { commitUrl: string | undefined; commitHash: string | undefined; - faCodeBranch = faCodeBranch; routerLink: string; localVCEnabled = false; - constructor( - private exerciseService: ExerciseService, - @Optional() private exerciseCacheService: ExerciseCacheService, - private profileService: ProfileService, - private router: Router, - ) {} - ngOnInit() { this.routerLink = this.router.url; this.result = this.participation.results?.[0]; diff --git a/src/main/webapp/app/exam/participate/summary/exercises/quiz-exam-summary/quiz-exam-summary.component.ts b/src/main/webapp/app/exam/participate/summary/exercises/quiz-exam-summary/quiz-exam-summary.component.ts index 217925a8e230..e29723a3585c 100644 --- a/src/main/webapp/app/exam/participate/summary/exercises/quiz-exam-summary/quiz-exam-summary.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exercises/quiz-exam-summary/quiz-exam-summary.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges } from '@angular/core'; +import { Component, Input, OnChanges, inject } from '@angular/core'; import dayjs from 'dayjs/esm'; import { QuizQuestionType } from 'app/entities/quiz/quiz-question.model'; import { QuizSubmission } from 'app/entities/quiz/quiz-submission.model'; @@ -8,18 +8,24 @@ import { ShortAnswerSubmittedText } from 'app/entities/quiz/short-answer-submitt import { MultipleChoiceSubmittedAnswer } from 'app/entities/quiz/multiple-choice-submitted-answer.model'; import { DragAndDropSubmittedAnswer } from 'app/entities/quiz/drag-and-drop-submitted-answer.model'; import { ShortAnswerSubmittedAnswer } from 'app/entities/quiz/short-answer-submitted-answer.model'; -import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.service'; import { Exam } from 'app/entities/exam/exam.model'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { Result } from 'app/entities/result.model'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; import { QuizParticipation } from 'app/entities/quiz/quiz-participation.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { MultipleChoiceQuestionComponent } from 'app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-question.component'; +import { DragAndDropQuestionComponent } from 'app/exercises/quiz/shared/questions/drag-and-drop-question/drag-and-drop-question.component'; +import { ShortAnswerQuestionComponent } from 'app/exercises/quiz/shared/questions/short-answer-question/short-answer-question.component'; @Component({ selector: 'jhi-quiz-exam-summary', templateUrl: './quiz-exam-summary.component.html', + imports: [TranslateDirective, MultipleChoiceQuestionComponent, DragAndDropQuestionComponent, ShortAnswerQuestionComponent], }) export class QuizExamSummaryComponent implements OnChanges { + private serverDateService = inject(ArtemisServerDateService); + readonly DRAG_AND_DROP = QuizQuestionType.DRAG_AND_DROP; readonly MULTIPLE_CHOICE = QuizQuestionType.MULTIPLE_CHOICE; readonly SHORT_ANSWER = QuizQuestionType.SHORT_ANSWER; @@ -28,26 +34,14 @@ export class QuizExamSummaryComponent implements OnChanges { dragAndDropMappings = new Map(); shortAnswerSubmittedTexts = new Map(); - @Input() - quizParticipation: QuizParticipation; - - @Input() - submission: QuizSubmission; - - @Input() - resultsPublished: boolean; - - @Input() - exam: Exam; + @Input() quizParticipation: QuizParticipation; + @Input() submission: QuizSubmission; + @Input() resultsPublished: boolean; + @Input() exam: Exam; result?: Result; - constructor( - private exerciseService: QuizExerciseService, - private serverDateService: ArtemisServerDateService, - ) {} - - ngOnChanges(): void { + ngOnChanges() { this.updateViewFromSubmission(); if (this.quizParticipation.studentParticipations) { this.result = diff --git a/src/main/webapp/app/exam/participate/summary/exercises/text-exam-summary/text-exam-summary.component.ts b/src/main/webapp/app/exam/participate/summary/exercises/text-exam-summary/text-exam-summary.component.ts index a1dcf5c9de34..d66da8d336e0 100644 --- a/src/main/webapp/app/exam/participate/summary/exercises/text-exam-summary/text-exam-summary.component.ts +++ b/src/main/webapp/app/exam/participate/summary/exercises/text-exam-summary/text-exam-summary.component.ts @@ -1,14 +1,16 @@ import { Component, Input } from '@angular/core'; import { TextSubmission } from 'app/entities/text/text-submission.model'; import { Exercise } from 'app/entities/exercise.model'; +import { TextEditorComponent } from 'app/exercises/text/participate/text-editor.component'; @Component({ selector: 'jhi-text-exam-summary', templateUrl: './text-exam-summary.component.html', + imports: [TextEditorComponent], }) export class TextExamSummaryComponent { @Input() exercise: Exercise; @Input() submission: TextSubmission; - @Input() expandProblemStatement?: boolean = false; - @Input() isAfterResultsArePublished?: boolean = false; + @Input() expandProblemStatement = false; + @Input() isAfterResultsArePublished = false; } diff --git a/src/main/webapp/app/exam/participate/summary/result-overview/exam-result-overview.component.ts b/src/main/webapp/app/exam/participate/summary/result-overview/exam-result-overview.component.ts index 86b3ae75d78a..ed923b879e6b 100644 --- a/src/main/webapp/app/exam/participate/summary/result-overview/exam-result-overview.component.ts +++ b/src/main/webapp/app/exam/participate/summary/result-overview/exam-result-overview.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, Input, OnChanges, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnChanges, OnInit, inject } from '@angular/core'; import { IncludedInOverallScore } from 'app/entities/exercise.model'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; @@ -11,6 +11,13 @@ import { roundScorePercentSpecifiedByCourseSettings } from 'app/shared/util/util import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { captureException } from '@sentry/angular'; import { isExamResultPublished } from 'app/exam/participate/exam.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CollapsibleCardComponent } from '../collapsible-card.component'; +import { NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NoDataComponent } from 'app/shared/no-data-component'; +import { GradingKeyTableComponent } from 'app/grading-system/grading-key-overview/grading-key/grading-key-table.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; type ExerciseInfo = { icon: IconProp; @@ -24,16 +31,21 @@ type ResultOverviewSection = 'grading-table' | 'grading-key' | 'bonus-grading-ke selector: 'jhi-exam-result-overview', styleUrls: ['./exam-result-overview.component.scss'], templateUrl: './exam-result-overview.component.html', + imports: [TranslateDirective, CollapsibleCardComponent, NgClass, FaIconComponent, NoDataComponent, GradingKeyTableComponent, ArtemisTranslatePipe], }) export class ExamResultOverviewComponent implements OnInit, OnChanges { + private serverDateService = inject(ArtemisServerDateService); + exerciseService = inject(ExerciseService); + private changeDetector = inject(ChangeDetectorRef); + readonly IncludedInOverallScore = IncludedInOverallScore; readonly BonusStrategy = BonusStrategy; @Input() studentExamWithGrade: StudentExamWithGradeDTO; - @Input() isGradingKeyCollapsed: boolean = true; - @Input() isBonusGradingKeyCollapsed: boolean = true; + @Input() isGradingKeyCollapsed = true; + @Input() isBonusGradingKeyCollapsed = true; @Input() exerciseInfos: Record; - @Input() isTestRun: boolean = false; + @Input() isTestRun = false; gradingScaleExists = false; isBonus = false; @@ -69,12 +81,6 @@ export class ExamResultOverviewComponent implements OnInit, OnChanges { 'bonus-grading-key': true, }; - constructor( - private serverDateService: ArtemisServerDateService, - public exerciseService: ExerciseService, - private changeDetector: ChangeDetectorRef, - ) {} - ngOnInit() { if (this.areResultsPublished()) { this.setExamGrade(); @@ -220,14 +226,6 @@ export class ExamResultOverviewComponent implements OnInit, OnChanges { return false; } - toggleGradingKey(): void { - this.isGradingKeyCollapsed = !this.isGradingKeyCollapsed; - } - - toggleBonusGradingKey(): void { - this.isBonusGradingKeyCollapsed = !this.isBonusGradingKeyCollapsed; - } - toggleCollapse(resultOverviewSection: ResultOverviewSection) { return () => (this.isCollapsed[resultOverviewSection] = !this.isCollapsed[resultOverviewSection]); } diff --git a/src/main/webapp/app/exam/participate/timer/exam-timer.component.ts b/src/main/webapp/app/exam/participate/timer/exam-timer.component.ts index 0df96822e1aa..1a5a53cf4d95 100644 --- a/src/main/webapp/app/exam/participate/timer/exam-timer.component.ts +++ b/src/main/webapp/app/exam/participate/timer/exam-timer.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output, inject } from '@angular/core'; import { Observable, Subject, timer } from 'rxjs'; import { distinctUntilChanged, first, map, takeUntil } from 'rxjs/operators'; import dayjs from 'dayjs/esm'; @@ -6,13 +6,18 @@ import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { cloneDeep } from 'lodash-es'; import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; import { round } from 'app/shared/util/utils'; +import { AsyncPipe } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exam-timer', templateUrl: './exam-timer.component.html', styleUrls: ['./exam-timer.scss'], + imports: [AsyncPipe, ArtemisTranslatePipe], }) export class ExamTimerComponent implements OnInit, OnDestroy { + private serverDateService = inject(ArtemisServerDateService); + @HostBinding('class.row') readonly row = true; @Input() @@ -39,7 +44,7 @@ export class ExamTimerComponent implements OnInit, OnDestroy { timePipe: ArtemisDurationFromSecondsPipe = new ArtemisDurationFromSecondsPipe(); - constructor(private serverDateService: ArtemisServerDateService) { + constructor() { this.timer .pipe( map((timeLeft: plugin.Duration) => timeLeft.asSeconds()), diff --git a/src/main/webapp/app/exam/participate/timer/exam-timer.module.ts b/src/main/webapp/app/exam/participate/timer/exam-timer.module.ts index b04ff5ab7a30..94c53564d044 100644 --- a/src/main/webapp/app/exam/participate/timer/exam-timer.module.ts +++ b/src/main/webapp/app/exam/participate/timer/exam-timer.module.ts @@ -4,8 +4,7 @@ import { ExamTimerComponent } from 'app/exam/participate/timer/exam-timer.compon import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @NgModule({ - declarations: [ExamTimerComponent], - imports: [CommonModule, ArtemisSharedCommonModule], + imports: [CommonModule, ArtemisSharedCommonModule, ExamTimerComponent], exports: [ExamTimerComponent], }) export class ArtemisExamTimerModule {} diff --git a/src/main/webapp/app/exam/shared/events/exam-live-event.component.ts b/src/main/webapp/app/exam/shared/events/exam-live-event.component.ts index ae2422c4f91f..64e4b60b1b0d 100644 --- a/src/main/webapp/app/exam/shared/events/exam-live-event.component.ts +++ b/src/main/webapp/app/exam/shared/events/exam-live-event.component.ts @@ -8,18 +8,26 @@ import { ProblemStatementUpdateEvent, WorkingTimeUpdateEvent, } from 'app/exam/participate/exam-participation-live-events.service'; +import { NgClass } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { WorkingTimeChangeComponent } from '../working-time-change/working-time-change.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-exam-live-event', templateUrl: './exam-live-event.component.html', styleUrls: ['./exam-live-event.component.scss'], + imports: [NgClass, TranslateDirective, FaIconComponent, WorkingTimeChangeComponent, ArtemisDatePipe, ArtemisTranslatePipe, HtmlForMarkdownPipe], }) export class ExamLiveEventComponent { @Input() event: ExamLiveEvent; @Input() - showAcknowledge: boolean = false; + showAcknowledge = false; @Output() onAcknowledge = new EventEmitter(); diff --git a/src/main/webapp/app/exam/shared/exam-shared.module.ts b/src/main/webapp/app/exam/shared/exam-shared.module.ts index 9b4db9b8f43b..68d31244d222 100644 --- a/src/main/webapp/app/exam/shared/exam-shared.module.ts +++ b/src/main/webapp/app/exam/shared/exam-shared.module.ts @@ -1,15 +1,22 @@ import { NgModule } from '@angular/core'; import { StudentExamWorkingTimeComponent } from 'app/exam/shared/student-exam-working-time/student-exam-working-time.component'; import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; -import { TestexamWorkingTimeComponent } from 'app/exam/shared/testExam-workingTime/testexam-working-time.component'; +import { TestExamWorkingTimeComponent } from 'app/exam/shared/testExam-workingTime/test-exam-working-time.component'; import { WorkingTimeControlComponent } from 'app/exam/shared/working-time-control/working-time-control.component'; import { ExamLiveEventComponent } from 'app/exam/shared/events/exam-live-event.component'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; import { WorkingTimeChangeComponent } from 'app/exam/shared/working-time-change/working-time-change.component'; @NgModule({ - imports: [ArtemisSharedCommonModule, ArtemisMarkdownModule], - declarations: [StudentExamWorkingTimeComponent, TestexamWorkingTimeComponent, WorkingTimeControlComponent, WorkingTimeChangeComponent, ExamLiveEventComponent], - exports: [StudentExamWorkingTimeComponent, TestexamWorkingTimeComponent, WorkingTimeControlComponent, WorkingTimeChangeComponent, ExamLiveEventComponent], + imports: [ + ArtemisSharedCommonModule, + ArtemisMarkdownModule, + StudentExamWorkingTimeComponent, + TestExamWorkingTimeComponent, + WorkingTimeControlComponent, + WorkingTimeChangeComponent, + ExamLiveEventComponent, + ], + exports: [StudentExamWorkingTimeComponent, TestExamWorkingTimeComponent, WorkingTimeControlComponent, WorkingTimeChangeComponent, ExamLiveEventComponent], }) export class ArtemisExamSharedModule {} diff --git a/src/main/webapp/app/exam/shared/student-exam-working-time/student-exam-working-time.component.ts b/src/main/webapp/app/exam/shared/student-exam-working-time/student-exam-working-time.component.ts index e32a0ae2afb2..e9e086cb872b 100644 --- a/src/main/webapp/app/exam/shared/student-exam-working-time/student-exam-working-time.component.ts +++ b/src/main/webapp/app/exam/shared/student-exam-working-time/student-exam-working-time.component.ts @@ -1,11 +1,13 @@ import { Component, Input, OnInit } from '@angular/core'; import { StudentExam } from 'app/entities/student-exam.model'; import { getRelativeWorkingTimeExtension } from 'app/exam/participate/exam.utils'; +import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; @Component({ selector: 'jhi-student-exam-working-time', templateUrl: './student-exam-working-time.component.html', providers: [], + imports: [ArtemisDurationFromSecondsPipe], }) export class StudentExamWorkingTimeComponent implements OnInit { @Input() studentExam: StudentExam; diff --git a/src/main/webapp/app/exam/shared/testExam-workingTime/testexam-working-time.component.html b/src/main/webapp/app/exam/shared/testExam-workingTime/test-exam-working-time.component.html similarity index 100% rename from src/main/webapp/app/exam/shared/testExam-workingTime/testexam-working-time.component.html rename to src/main/webapp/app/exam/shared/testExam-workingTime/test-exam-working-time.component.html diff --git a/src/main/webapp/app/exam/shared/testExam-workingTime/testexam-working-time.component.ts b/src/main/webapp/app/exam/shared/testExam-workingTime/test-exam-working-time.component.ts similarity index 81% rename from src/main/webapp/app/exam/shared/testExam-workingTime/testexam-working-time.component.ts rename to src/main/webapp/app/exam/shared/testExam-workingTime/test-exam-working-time.component.ts index 0de8831f3fbe..e7b8bca26088 100644 --- a/src/main/webapp/app/exam/shared/testExam-workingTime/testexam-working-time.component.ts +++ b/src/main/webapp/app/exam/shared/testExam-workingTime/test-exam-working-time.component.ts @@ -2,13 +2,15 @@ import { Component, Input, OnInit } from '@angular/core'; import { StudentExam } from 'app/entities/student-exam.model'; import { round } from 'app/shared/util/utils'; import dayjs from 'dayjs/esm'; +import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; @Component({ - selector: 'jhi-testexam-working-time', - templateUrl: './testexam-working-time.component.html', + selector: 'jhi-test-exam-working-time', + templateUrl: './test-exam-working-time.component.html', providers: [], + imports: [ArtemisDurationFromSecondsPipe], }) -export class TestexamWorkingTimeComponent implements OnInit { +export class TestExamWorkingTimeComponent implements OnInit { @Input() studentExam: StudentExam; percentUsedWorkingTime = 0; diff --git a/src/main/webapp/app/exam/shared/working-time-change/working-time-change.component.ts b/src/main/webapp/app/exam/shared/working-time-change/working-time-change.component.ts index c5eec114e2e6..919ee6a667be 100644 --- a/src/main/webapp/app/exam/shared/working-time-change/working-time-change.component.ts +++ b/src/main/webapp/app/exam/shared/working-time-change/working-time-change.component.ts @@ -1,8 +1,11 @@ import { Component, Input } from '@angular/core'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; @Component({ selector: 'jhi-working-time-change', templateUrl: './working-time-change.component.html', + imports: [TranslateDirective, ArtemisDurationFromSecondsPipe], }) export class WorkingTimeChangeComponent { @Input() oldWorkingTime: number; diff --git a/src/main/webapp/app/exam/shared/working-time-control/working-time-control.component.ts b/src/main/webapp/app/exam/shared/working-time-control/working-time-control.component.ts index 4d90cf31e917..4250a0f3c727 100644 --- a/src/main/webapp/app/exam/shared/working-time-control/working-time-control.component.ts +++ b/src/main/webapp/app/exam/shared/working-time-control/working-time-control.component.ts @@ -1,10 +1,11 @@ -import { Component, Input } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Component, Input, inject } from '@angular/core'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Exam } from 'app/entities/exam/exam.model'; import { round } from 'app/shared/util/utils'; import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; import { getRelativeWorkingTimeExtension } from 'app/exam/participate/exam.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-working-time-control', @@ -18,8 +19,11 @@ import { getRelativeWorkingTimeExtension } from 'app/exam/participate/exam.utils useExisting: WorkingTimeControlComponent, }, ], + imports: [TranslateDirective, FormsModule], }) export class WorkingTimeControlComponent implements ControlValueAccessor { + private artemisDurationFromSecondsPipe = inject(ArtemisDurationFromSecondsPipe); + // Control disabled state @Input() disabled = false; @Input() allowNegative = false; @@ -56,8 +60,6 @@ export class WorkingTimeControlComponent implements ControlValueAccessor { private onTouched = () => {}; private onChange: (_: number) => void = () => {}; - constructor(private artemisDurationFromSecondsPipe: ArtemisDurationFromSecondsPipe) {} - /** * Updates the working time duration inputs whenever * the value of the form control changes. diff --git a/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.component.ts b/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.component.ts index e259fd3ea284..1a75f8c43aec 100644 --- a/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.component.ts +++ b/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.component.ts @@ -1,7 +1,7 @@ -import { Location } from '@angular/common'; +import { Location, UpperCasePipe } from '@angular/common'; import { HttpErrorResponse } from '@angular/common/http'; -import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation, inject } from '@angular/core'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { TranslateService } from '@ngx-translate/core'; import { isAllowedToModifyFeedback } from 'app/assessment/assessment.service'; @@ -24,19 +24,51 @@ import { getPositiveAndCappedTotalScore, getTotalMaxPoints } from 'app/exercises import { assessmentNavigateBack } from 'app/exercises/shared/navigate-back.util'; import { StructuredGradingCriterionService } from 'app/exercises/shared/structured-grading-criterion/structured-grading-criterion.service'; import { SubmissionService } from 'app/exercises/shared/submission/submission.service'; +import { UnreferencedFeedbackComponent } from 'app/exercises/shared/unreferenced-feedback/unreferenced-feedback.component'; import { FileService } from 'app/shared/http/file.service'; import { onError } from 'app/shared/util/global.utils'; import { getExerciseDashboardLink, getLinkToSubmissionAssessment } from 'app/utils/navigation.utils'; import dayjs from 'dayjs/esm'; import { filter, finalize } from 'rxjs/operators'; +import { AssessmentLayoutComponent } from 'app/assessment/assessment-layout/assessment-layout.component'; +import { ResizeableContainerComponent } from 'app/shared/resizeable-container/resizeable-container.component'; +import { ScoreDisplayComponent } from 'app/shared/score-display/score-display.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { AssessmentInstructionsComponent } from 'app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ providers: [FileUploadAssessmentService], templateUrl: './file-upload-assessment.component.html', - styles: [], encapsulation: ViewEncapsulation.None, + imports: [ + AssessmentLayoutComponent, + ResizeableContainerComponent, + ScoreDisplayComponent, + TranslateDirective, + FaIconComponent, + AssessmentInstructionsComponent, + UnreferencedFeedbackComponent, + RouterLink, + UpperCasePipe, + ArtemisTranslatePipe, + ], }) export class FileUploadAssessmentComponent implements OnInit, OnDestroy { + private changeDetectorRef = inject(ChangeDetectorRef); + private alertService = inject(AlertService); + private router = inject(Router); + private route = inject(ActivatedRoute); + private fileUploadAssessmentService = inject(FileUploadAssessmentService); + private accountService = inject(AccountService); + private location = inject(Location); + private fileUploadSubmissionService = inject(FileUploadSubmissionService); + private complaintService = inject(ComplaintService); + private fileService = inject(FileService); + structuredGradingCriterionService = inject(StructuredGradingCriterionService); + submissionService = inject(SubmissionService); + text: string; participation: StudentParticipation; submission?: FileUploadSubmission; @@ -73,21 +105,9 @@ export class FileUploadAssessmentComponent implements OnInit, OnDestroy { // Icons farListAlt = faListAlt; - constructor( - private changeDetectorRef: ChangeDetectorRef, - private alertService: AlertService, - private router: Router, - private route: ActivatedRoute, - private fileUploadAssessmentService: FileUploadAssessmentService, - private accountService: AccountService, - private location: Location, - private fileUploadSubmissionService: FileUploadSubmissionService, - private complaintService: ComplaintService, - private fileService: FileService, - public structuredGradingCriterionService: StructuredGradingCriterionService, - public submissionService: SubmissionService, - translateService: TranslateService, - ) { + constructor() { + const translateService = inject(TranslateService); + this.assessmentsAreValid = false; translateService.get('artemisApp.assessment.messages.confirmCancel').subscribe((text) => (this.cancelConfirmationText = text)); } diff --git a/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.module.ts b/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.module.ts index bb16e7ebf643..7a65e727ac23 100644 --- a/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.module.ts +++ b/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.module.ts @@ -21,7 +21,7 @@ import { ArtemisAssessmentProgressLabelModule } from 'app/exercises/shared/asses ArtemisMarkdownModule, SubmissionResultStatusModule, ArtemisAssessmentProgressLabelModule, + FileUploadAssessmentComponent, ], - declarations: [FileUploadAssessmentComponent], }) export class ArtemisFileUploadAssessmentModule {} diff --git a/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.route.ts b/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.route.ts index 7043cf832c72..1acf7913fb87 100644 --- a/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.route.ts +++ b/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.route.ts @@ -1,13 +1,13 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { FileUploadAssessmentComponent } from 'app/exercises/file-upload/assess/file-upload-assessment.component'; + import { Authority } from 'app/shared/constants/authority.constants'; export const routes: Routes = [ { path: ':courseId/file-upload-exercises/:exerciseId/submissions/:submissionId/assessment', - component: FileUploadAssessmentComponent, + loadComponent: () => import('app/exercises/file-upload/assess/file-upload-assessment.component').then((m) => m.FileUploadAssessmentComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.fileUploadExercise.home.title', @@ -16,7 +16,7 @@ export const routes: Routes = [ }, { path: ':courseId/file-upload-exercises/:exerciseId/submissions/:submissionId/assessments/:resultId', - component: FileUploadAssessmentComponent, + loadComponent: () => import('app/exercises/file-upload/assess/file-upload-assessment.component').then((m) => m.FileUploadAssessmentComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.fileUploadExercise.home.title', diff --git a/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.service.ts b/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.service.ts index b109fcb079b9..a2265eb1c59e 100644 --- a/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.service.ts +++ b/src/main/webapp/app/exercises/file-upload/assess/file-upload-assessment.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { ComplaintResponse } from 'app/entities/complaint-response.model'; @@ -14,9 +14,9 @@ type FileUploadAssessmentDTO = { feedbacks: Feedback[]; assessmentNote?: string providedIn: 'root', }) export class FileUploadAssessmentService { - private resourceUrl = 'api'; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + private resourceUrl = 'api'; saveAssessment(feedbacks: Feedback[], submissionId: number, assessmentNote: string | undefined, submit = false): Observable { let params = new HttpParams(); diff --git a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-detail.component.ts b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-detail.component.ts index abb0c24df3ba..e23ce8f5d636 100644 --- a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-detail.component.ts +++ b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-detail.component.ts @@ -1,7 +1,9 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { SafeHtml } from '@angular/platform-browser'; +import { NonProgrammingExerciseDetailCommonActionsComponent } from 'app/exercises/shared/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.component'; +import { ExerciseDetailStatisticsComponent } from 'app/exercises/shared/statistics/exercise-detail-statistics.component'; import { Subscription } from 'rxjs'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { FileUploadExerciseService } from './file-upload-exercise.service'; @@ -25,12 +27,23 @@ import { getExerciseProblemDetailSection, } from 'app/exercises/shared/utils'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { DetailOverviewListComponent } from 'app/detail-overview-list/detail-overview-list.component'; @Component({ selector: 'jhi-file-upload-exercise-detail', templateUrl: './file-upload-exercise-detail.component.html', + imports: [TranslateDirective, DocumentationButtonComponent, NonProgrammingExerciseDetailCommonActionsComponent, ExerciseDetailStatisticsComponent, DetailOverviewListComponent], }) export class FileUploadExerciseDetailComponent implements OnInit, OnDestroy { + private eventManager = inject(EventManager); + private fileUploadExerciseService = inject(FileUploadExerciseService); + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + private statisticsService = inject(StatisticsService); + private artemisMarkdown = inject(ArtemisMarkdownService); + readonly documentationType: DocumentationType = 'FileUpload'; readonly ExerciseType = ExerciseType; readonly dayjs = dayjs; @@ -47,15 +60,6 @@ export class FileUploadExerciseDetailComponent implements OnInit, OnDestroy { formattedExampleSolution: SafeHtml | null; formattedGradingInstructions: SafeHtml | null; - constructor( - private eventManager: EventManager, - private fileUploadExerciseService: FileUploadExerciseService, - private route: ActivatedRoute, - private alertService: AlertService, - private statisticsService: StatisticsService, - private artemisMarkdown: ArtemisMarkdownService, - ) {} - /** * Initializes subscription for file upload exercise */ diff --git a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management-resolve.service.ts b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management-resolve.service.ts index ae506ef9ffa6..353bf5a1c5b1 100644 --- a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management-resolve.service.ts +++ b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management-resolve.service.ts @@ -4,18 +4,16 @@ import { CourseManagementService } from 'app/course/manage/course-management.ser import { ExerciseGroupService } from 'app/exam/manage/exercise-groups/exercise-group.service'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Course } from 'app/entities/course.model'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; import { HttpResponse } from '@angular/common/http'; import { filter, map, of } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class FileUploadExerciseManagementResolve implements Resolve { - constructor( - private fileUploadExerciseService: FileUploadExerciseService, - private courseService: CourseManagementService, - private exerciseGroupService: ExerciseGroupService, - ) {} + private fileUploadExerciseService = inject(FileUploadExerciseService); + private courseService = inject(CourseManagementService); + private exerciseGroupService = inject(ExerciseGroupService); /** * Resolves the route and initializes file upload exercise either from exerciseId (existing exercise) or diff --git a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management.module.ts b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management.module.ts index be4a8a832e6f..2b9a93a12606 100644 --- a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management.module.ts +++ b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management.module.ts @@ -22,7 +22,7 @@ import { ExerciseTitleChannelNameModule } from 'app/exercises/shared/exercise-ti import { ExerciseUpdateNotificationModule } from 'app/exercises/shared/exercise-update-notification/exercise-update-notification.module'; import { DetailModule } from 'app/detail-overview-list/detail.module'; import { ArtemisExerciseModule } from 'app/exercises/shared/exercise/exercise.module'; -import { FormsModule } from 'app/forms/forms.module'; +import { ArtemisFormsModule } from 'app/forms/artemis-forms.module'; @NgModule({ imports: [ @@ -46,9 +46,11 @@ import { FormsModule } from 'app/forms/forms.module'; ExerciseTitleChannelNameModule, ExerciseUpdateNotificationModule, DetailModule, - FormsModule, + ArtemisFormsModule, + FileUploadExerciseComponent, + FileUploadExerciseDetailComponent, + FileUploadExerciseUpdateComponent, ], - declarations: [FileUploadExerciseComponent, FileUploadExerciseDetailComponent, FileUploadExerciseUpdateComponent], exports: [FileUploadExerciseComponent], }) export class ArtemisFileUploadExerciseManagementModule {} diff --git a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management.route.ts b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management.route.ts index 30a829a6ebc5..115357a32ba6 100644 --- a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management.route.ts +++ b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-management.route.ts @@ -1,16 +1,16 @@ import { RouterModule, Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { FileUploadExerciseDetailComponent } from './file-upload-exercise-detail.component'; + import { NgModule } from '@angular/core'; -import { FileUploadExerciseUpdateComponent } from 'app/exercises/file-upload/manage/file-upload-exercise-update.component'; + import { Authority } from 'app/shared/constants/authority.constants'; -import { ExerciseStatisticsComponent } from 'app/exercises/shared/statistics/exercise-statistics.component'; + import { FileUploadExerciseManagementResolve } from 'app/exercises/file-upload/manage/file-upload-exercise-management-resolve.service'; const routes: Routes = [ { path: ':courseId/file-upload-exercises/new', - component: FileUploadExerciseUpdateComponent, + loadComponent: () => import('app/exercises/file-upload/manage/file-upload-exercise-update.component').then((m) => m.FileUploadExerciseUpdateComponent), resolve: { fileUploadExercise: FileUploadExerciseManagementResolve, }, @@ -22,7 +22,7 @@ const routes: Routes = [ }, { path: ':courseId/file-upload-exercises/:exerciseId/import', - component: FileUploadExerciseUpdateComponent, + loadComponent: () => import('app/exercises/file-upload/manage/file-upload-exercise-update.component').then((m) => m.FileUploadExerciseUpdateComponent), resolve: { fileUploadExercise: FileUploadExerciseManagementResolve, }, @@ -35,7 +35,7 @@ const routes: Routes = [ { path: ':courseId/file-upload-exercises/:exerciseId/edit', - component: FileUploadExerciseUpdateComponent, + loadComponent: () => import('app/exercises/file-upload/manage/file-upload-exercise-update.component').then((m) => m.FileUploadExerciseUpdateComponent), resolve: { fileUploadExercise: FileUploadExerciseManagementResolve, }, @@ -47,7 +47,7 @@ const routes: Routes = [ }, { path: ':courseId/file-upload-exercises/:exerciseId', - component: FileUploadExerciseDetailComponent, + loadComponent: () => import('./file-upload-exercise-detail.component').then((m) => m.FileUploadExerciseDetailComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.fileUploadExercise.home.title', @@ -60,7 +60,7 @@ const routes: Routes = [ }, { path: ':courseId/file-upload-exercises/:exerciseId/exercise-statistics', - component: ExerciseStatisticsComponent, + loadComponent: () => import('app/exercises/shared/statistics/exercise-statistics.component').then((m) => m.ExerciseStatisticsComponent), resolve: { fileUploadExercise: FileUploadExerciseManagementResolve, }, diff --git a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-paging.service.ts b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-paging.service.ts index e2d97cfa5934..cf144194dd52 100644 --- a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-paging.service.ts +++ b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-paging.service.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ExercisePagingService } from 'app/exercises/shared/manage/exercise-paging.service'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; @@ -7,7 +7,9 @@ import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; export class FileUploadExercisePagingService extends ExercisePagingService { private static readonly RESOURCE_URL = 'api/file-upload-exercises'; - constructor(http: HttpClient) { + constructor() { + const http = inject(HttpClient); + super(http, FileUploadExercisePagingService.RESOURCE_URL); } } diff --git a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-update.component.ts b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-update.component.ts index 798a1d50f57d..cf61a8e45700 100644 --- a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-update.component.ts +++ b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise-update.component.ts @@ -1,7 +1,11 @@ -import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { ActivatedRoute, Params } from '@angular/router'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { AlertService, AlertType } from 'app/core/util/alert.service'; +import { DifficultyPickerComponent } from 'app/exercises/shared/difficulty-picker/difficulty-picker.component'; +import { IncludedInOverallScorePickerComponent } from 'app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.component'; +import { PresentationScoreComponent } from 'app/exercises/shared/presentation-score/presentation-score.component'; +import { GradingInstructionsDetailsComponent } from 'app/exercises/shared/structured-grading-criterion/grading-instructions-details/grading-instructions-details.component'; import { FileUploadExerciseService } from './file-upload-exercise.service'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; @@ -10,7 +14,7 @@ import { Exercise, ExerciseMode, IncludedInOverallScore, getCourseId, resetForIm import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; import { cloneDeep } from 'lodash-es'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { ExerciseUpdateWarningService } from 'app/exercises/shared/exercise-update-warning/exercise-update-warning.service'; import { onError } from 'app/shared/util/global.utils'; import { EditType, SaveExerciseCommand } from 'app/exercises/shared/exercise/exercise.utils'; @@ -22,17 +26,62 @@ import { scrollToTopOfPage } from 'app/shared/util/utils'; import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; import { ExerciseTitleChannelNameComponent } from 'app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component'; import { TeamConfigFormGroupComponent } from 'app/exercises/shared/team-config-form-group/team-config-form-group.component'; -import { NgModel } from '@angular/forms'; +import { FormsModule, NgModel } from '@angular/forms'; import { Subscription } from 'rxjs'; import { FormSectionStatus } from 'app/forms/form-status-bar/form-status-bar.component'; import { FormulaAction } from 'app/shared/monaco-editor/model/actions/formula.action'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { FormStatusBarComponent } from 'app/forms/form-status-bar/form-status-bar.component'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { CategorySelectorComponent } from 'app/shared/category-selector/category-selector.component'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { CompetencySelectionComponent } from 'app/shared/competency-selection/competency-selection.component'; +import { CustomMinDirective } from 'app/shared/validators/custom-min-validator.directive'; +import { CustomMaxDirective } from 'app/shared/validators/custom-max-validator.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormFooterComponent } from 'app/forms/form-footer/form-footer.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-file-upload-exercise-update', templateUrl: './file-upload-exercise-update.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FormsModule, + TranslateDirective, + DocumentationButtonComponent, + FormStatusBarComponent, + ExerciseTitleChannelNameComponent, + HelpIconComponent, + CategorySelectorComponent, + DifficultyPickerComponent, + TeamConfigFormGroupComponent, + MarkdownEditorMonacoComponent, + CompetencySelectionComponent, + FormDateTimePickerComponent, + IncludedInOverallScorePickerComponent, + CustomMinDirective, + CustomMaxDirective, + FaIconComponent, + NgbTooltip, + PresentationScoreComponent, + GradingInstructionsDetailsComponent, + FormFooterComponent, + ArtemisTranslatePipe, + ], }) export class FileUploadExerciseUpdateComponent implements AfterViewInit, OnDestroy, OnInit { + private fileUploadExerciseService = inject(FileUploadExerciseService); + private modalService = inject(NgbModal); + private popupService = inject(ExerciseUpdateWarningService); + private activatedRoute = inject(ActivatedRoute); + private courseService = inject(CourseManagementService); + private exerciseService = inject(ExerciseService); + private alertService = inject(AlertService); + private navigationUtilService = inject(ArtemisNavigationUtilService); + private exerciseGroupService = inject(ExerciseGroupService); + protected readonly faQuestionCircle = faQuestionCircle; readonly IncludedInOverallScore = IncludedInOverallScore; @@ -68,18 +117,6 @@ export class FileUploadExerciseUpdateComponent implements AfterViewInit, OnDestr bonusPointsSubscription?: Subscription; teamSubscription?: Subscription; - constructor( - private fileUploadExerciseService: FileUploadExerciseService, - private modalService: NgbModal, - private popupService: ExerciseUpdateWarningService, - private activatedRoute: ActivatedRoute, - private courseService: CourseManagementService, - private exerciseService: ExerciseService, - private alertService: AlertService, - private navigationUtilService: ArtemisNavigationUtilService, - private exerciseGroupService: ExerciseGroupService, - ) {} - get editType(): EditType { if (this.isImport) { return EditType.IMPORT; diff --git a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise.component.ts b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise.component.ts index 0874dea0faf6..9d6e49fad41a 100644 --- a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise.component.ts +++ b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise.component.ts @@ -12,10 +12,20 @@ import { AlertService } from 'app/core/util/alert.service'; import { faBook, faPlus, faSort, faTable, faTrash, faUsers, faWrench } from '@fortawesome/free-solid-svg-icons'; import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { CourseExerciseService } from 'app/exercises/shared/course-exercises/course-exercise.service'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { FormsModule } from '@angular/forms'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { RouterLink } from '@angular/router'; +import { ExerciseCategoriesComponent } from 'app/shared/exercise-categories/exercise-categories.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-file-upload-exercise', templateUrl: './file-upload-exercise.component.html', + imports: [SortDirective, FormsModule, SortByDirective, TranslateDirective, FaIconComponent, RouterLink, ExerciseCategoriesComponent, DeleteButtonDirective, ArtemisDatePipe], }) export class FileUploadExerciseComponent extends ExerciseComponent { protected exerciseService = inject(ExerciseService); diff --git a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise.service.ts b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise.service.ts index cd19cb977939..06339252611e 100644 --- a/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise.service.ts +++ b/src/main/webapp/app/exercises/file-upload/manage/file-upload-exercise.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -12,12 +12,10 @@ export type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class FileUploadExerciseService implements ExerciseServicable { - private resourceUrl = 'api/file-upload-exercises'; + private http = inject(HttpClient); + private exerciseService = inject(ExerciseService); - constructor( - private http: HttpClient, - private exerciseService: ExerciseService, - ) {} + private resourceUrl = 'api/file-upload-exercises'; /** * Sends request to create new file upload exercise diff --git a/src/main/webapp/app/exercises/file-upload/participate/file-upload-participation.module.ts b/src/main/webapp/app/exercises/file-upload/participate/file-upload-participation.module.ts index 19a5d3e8d7fe..26f24f2ea86e 100644 --- a/src/main/webapp/app/exercises/file-upload/participate/file-upload-participation.module.ts +++ b/src/main/webapp/app/exercises/file-upload/participate/file-upload-participation.module.ts @@ -19,8 +19,8 @@ import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; ArtemisHeaderExercisePageWithDetailsModule, RatingModule, ArtemisMarkdownModule, + FileUploadSubmissionComponent, ], - declarations: [FileUploadSubmissionComponent], exports: [FileUploadSubmissionComponent], }) export class ArtemisFileUploadParticipationModule {} diff --git a/src/main/webapp/app/exercises/file-upload/participate/file-upload-participation.route.ts b/src/main/webapp/app/exercises/file-upload/participate/file-upload-participation.route.ts index 7cfcb7220534..5b047f5097a0 100644 --- a/src/main/webapp/app/exercises/file-upload/participate/file-upload-participation.route.ts +++ b/src/main/webapp/app/exercises/file-upload/participate/file-upload-participation.route.ts @@ -2,13 +2,13 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { PendingChangesGuard } from 'app/shared/guard/pending-changes.guard'; -import { FileUploadSubmissionComponent } from 'app/exercises/file-upload/participate/file-upload-submission.component'; + import { Authority } from 'app/shared/constants/authority.constants'; export const routes: Routes = [ { path: 'participate/:participationId', - component: FileUploadSubmissionComponent, + loadComponent: () => import('app/exercises/file-upload/participate/file-upload-submission.component').then((m) => m.FileUploadSubmissionComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.fileUploadExercise.home.title', diff --git a/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts b/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts index 2157ebc343b1..f4d6c3121f61 100644 --- a/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts +++ b/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts @@ -1,9 +1,11 @@ -import { Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; -import { Location } from '@angular/common'; +import { Component, ElementRef, Input, OnInit, ViewChild, inject } from '@angular/core'; +import { Location, UpperCasePipe } from '@angular/common'; import { TranslateService } from '@ngx-translate/core'; import { ActivatedRoute } from '@angular/router'; import { HttpErrorResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; +import { HeaderParticipationPageComponent } from 'app/exercises/shared/exercise-headers/header-participation-page.component'; +import { RatingComponent } from 'app/exercises/shared/rating/rating.component'; import dayjs from 'dayjs/esm'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { FileUploadSubmissionService } from 'app/exercises/file-upload/participate/file-upload-submission.service'; @@ -29,19 +31,56 @@ import { getCourseFromExercise } from 'app/entities/exercise.model'; import { Course } from 'app/entities/course.model'; import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { faDownload } from '@fortawesome/free-solid-svg-icons'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { ResizeableContainerComponent } from 'app/shared/resizeable-container/resizeable-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ExerciseActionButtonComponent } from 'app/shared/components/exercise-action-button.component'; +import { AdditionalFeedbackComponent } from 'app/shared/additional-feedback/additional-feedback.component'; +import { ComplaintsStudentViewComponent } from 'app/complaints/complaints-for-students/complaints-student-view.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-file-upload-submission', templateUrl: './file-upload-submission.component.html', + imports: [ + HeaderParticipationPageComponent, + ButtonComponent, + ResizeableContainerComponent, + TranslateDirective, + ExerciseActionButtonComponent, + AdditionalFeedbackComponent, + RatingComponent, + ComplaintsStudentViewComponent, + FaIconComponent, + UpperCasePipe, + ArtemisTranslatePipe, + ArtemisTimeAgoPipe, + HtmlForMarkdownPipe, + ], }) export class FileUploadSubmissionComponent implements OnInit, ComponentCanDeactivate { + private route = inject(ActivatedRoute); + private fileUploadSubmissionService = inject(FileUploadSubmissionService); + private fileUploaderService = inject(FileUploaderService); + private resultService = inject(ResultService); + private alertService = inject(AlertService); + private location = inject(Location); + private translateService = inject(TranslateService); + private fileService = inject(FileService); + private participationWebsocketService = inject(ParticipationWebsocketService); + private fileUploadAssessmentService = inject(FileUploadAssessmentService); + private accountService = inject(AccountService); + readonly addParticipationToResult = addParticipationToResult; @ViewChild('fileInput', { static: false }) fileInput: ElementRef; @Input() participationId?: number; - @Input() displayHeader: boolean = true; - @Input() expandProblemStatement?: boolean = true; - @Input() displayedInExamSummary?: boolean = false; + @Input() displayHeader = true; + @Input() expandProblemStatement = true; + @Input() displayedInExamSummary = false; @Input() inputExercise?: FileUploadExercise; @Input() inputSubmission?: FileUploadSubmission; @@ -74,19 +113,9 @@ export class FileUploadSubmissionComponent implements OnInit, ComponentCanDeacti // Icons farListAlt = faListAlt; - constructor( - private route: ActivatedRoute, - private fileUploadSubmissionService: FileUploadSubmissionService, - private fileUploaderService: FileUploaderService, - private resultService: ResultService, - private alertService: AlertService, - private location: Location, - private translateService: TranslateService, - private fileService: FileService, - private participationWebsocketService: ParticipationWebsocketService, - private fileUploadAssessmentService: FileUploadAssessmentService, - private accountService: AccountService, - ) { + constructor() { + const translateService = this.translateService; + translateService.get('artemisApp.fileUploadSubmission.confirmSubmission').subscribe((text) => (this.submissionConfirmationText = text)); } diff --git a/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.service.ts b/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.service.ts index b685eae92bc2..0d961a76caaa 100644 --- a/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.service.ts +++ b/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -12,10 +12,8 @@ export type EntityResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class FileUploadSubmissionService { - constructor( - private http: HttpClient, - private submissionService: SubmissionService, - ) {} + private http = inject(HttpClient); + private submissionService = inject(SubmissionService); /** * Updates File Upload submission on the server diff --git a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component.ts b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component.ts index fe7a6017459f..9b30052e6905 100644 --- a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component.ts +++ b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component.ts @@ -1,9 +1,10 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { Location } from '@angular/common'; +import { UnreferencedFeedbackComponent } from 'app/exercises/shared/unreferenced-feedback/unreferenced-feedback.component'; import { firstValueFrom } from 'rxjs'; import { AlertService } from 'app/core/util/alert.service'; import { UMLDiagramType, UMLModel } from '@ls1intum/apollon'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { AccountService } from 'app/core/auth/account.service'; import { HttpErrorResponse } from '@angular/common/http'; import { TranslateService } from '@ngx-translate/core'; @@ -31,13 +32,45 @@ import { isAllowedToModifyFeedback } from 'app/assessment/assessment.service'; import { AssessmentAfterComplaint } from 'app/complaints/complaints-for-tutor/complaints-for-tutor.component'; import { AthenaService } from 'app/assessment/athena.service'; import { faCircleNotch, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; +import { AssessmentLayoutComponent } from 'app/assessment/assessment-layout/assessment-layout.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ModelingAssessmentComponent } from '../modeling-assessment.component'; +import { CollapsableAssessmentInstructionsComponent } from 'app/assessment/assessment-instructions/collapsable-assessment-instructions/collapsable-assessment-instructions.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-modeling-assessment-editor', templateUrl: './modeling-assessment-editor.component.html', styleUrls: ['./modeling-assessment-editor.component.scss'], + imports: [ + AssessmentLayoutComponent, + TranslateDirective, + NgbTooltip, + FaIconComponent, + ModelingAssessmentComponent, + CollapsableAssessmentInstructionsComponent, + UnreferencedFeedbackComponent, + RouterLink, + ArtemisTranslatePipe, + ], }) export class ModelingAssessmentEditorComponent implements OnInit { + private alertService = inject(AlertService); + private router = inject(Router); + private route = inject(ActivatedRoute); + private modelingSubmissionService = inject(ModelingSubmissionService); + private modelingAssessmentService = inject(ModelingAssessmentService); + private accountService = inject(AccountService); + private location = inject(Location); + private translateService = inject(TranslateService); + private complaintService = inject(ComplaintService); + private structuredGradingCriterionService = inject(StructuredGradingCriterionService); + private submissionService = inject(SubmissionService); + private exampleSubmissionService = inject(ExampleSubmissionService); + private athenaService = inject(AthenaService); + totalScore = 0; submission?: ModelingSubmission; model?: UMLModel; @@ -78,21 +111,9 @@ export class ModelingAssessmentEditorComponent implements OnInit { protected readonly faCircleNotch = faCircleNotch; protected readonly faQuestionCircle = faQuestionCircle; - constructor( - private alertService: AlertService, - private router: Router, - private route: ActivatedRoute, - private modelingSubmissionService: ModelingSubmissionService, - private modelingAssessmentService: ModelingAssessmentService, - private accountService: AccountService, - private location: Location, - private translateService: TranslateService, - private complaintService: ComplaintService, - private structuredGradingCriterionService: StructuredGradingCriterionService, - private submissionService: SubmissionService, - private exampleSubmissionService: ExampleSubmissionService, - private athenaService: AthenaService, - ) { + constructor() { + const translateService = this.translateService; + translateService.get('artemisApp.modelingAssessmentEditor.messages.confirmCancel').subscribe((text) => (this.cancelConfirmationText = text)); } diff --git a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.module.ts b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.module.ts index b52ddeaee956..f7b7326a1670 100644 --- a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.module.ts +++ b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.module.ts @@ -21,8 +21,8 @@ import { ArtemisAssessmentProgressLabelModule } from 'app/exercises/shared/asses ModelingAssessmentModule, SubmissionResultStatusModule, ArtemisAssessmentProgressLabelModule, + ModelingAssessmentEditorComponent, ], - declarations: [ModelingAssessmentEditorComponent], exports: [ModelingAssessmentEditorComponent], }) export class ArtemisModelingAssessmentEditorModule {} diff --git a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.route.ts b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.route.ts index 526c30454334..2c21e3200e1a 100644 --- a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.route.ts +++ b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.route.ts @@ -1,13 +1,14 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { ModelingAssessmentEditorComponent } from 'app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component'; + import { Authority } from 'app/shared/constants/authority.constants'; export const routes: Routes = [ { path: ':courseId/modeling-exercises/:exerciseId/submissions/:submissionId/assessment', - component: ModelingAssessmentEditorComponent, + loadComponent: () => + import('app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component').then((m) => m.ModelingAssessmentEditorComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.modelingExercise.home.title', @@ -16,7 +17,8 @@ export const routes: Routes = [ }, { path: ':courseId/modeling-exercises/:exerciseId/submissions/:submissionId/assessments/:resultId', - component: ModelingAssessmentEditorComponent, + loadComponent: () => + import('app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component').then((m) => m.ModelingAssessmentEditorComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], usePathForBreadcrumbs: true, diff --git a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.component.ts b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.component.ts index d39b9a9f1830..0944dd628d07 100644 --- a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.component.ts +++ b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges } from '@angular/core'; +import { AfterViewInit, Component, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges, inject } from '@angular/core'; import { ApollonEditor, ApollonMode, Assessment, Selection, UMLDiagramType, UMLElementType, UMLModel, UMLRelationshipType, addOrUpdateAssessment } from '@ls1intum/apollon'; import { Feedback, FeedbackType } from 'app/entities/feedback.model'; import { ModelElementCount } from 'app/entities/modeling-submission.model'; @@ -7,6 +7,9 @@ import { Course } from 'app/entities/course.model'; import { GradingInstruction } from 'app/exercises/shared/structured-grading-criterion/grading-instruction.model'; import { ModelingComponent } from 'app/exercises/modeling/shared/modeling.component'; import { filterInvalidFeedback } from 'app/exercises/modeling/assess/modeling-assessment.util'; +import { ScoreDisplayComponent } from 'app/shared/score-display/score-display.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ModelingExplanationEditorComponent } from '../shared/modeling-explanation-editor.component'; export interface DropInfo { instruction: GradingInstruction; @@ -19,8 +22,11 @@ export interface DropInfo { selector: 'jhi-modeling-assessment', templateUrl: './modeling-assessment.component.html', styleUrls: ['./modeling-assessment.component.scss'], + imports: [ScoreDisplayComponent, FaIconComponent, ModelingExplanationEditorComponent], }) export class ModelingAssessmentComponent extends ModelingComponent implements AfterViewInit, OnDestroy, OnChanges { + private artemisTranslatePipe = inject(ArtemisTranslatePipe); + @Input() maxScore: number; @Input() maxBonusPoints = 0; @Input() totalScore: number; @@ -49,7 +55,7 @@ export class ModelingAssessmentComponent extends ModelingComponent implements Af firstCorrectionRoundColor = '#3e8acc'; secondCorrectionRoundColor = '#ffa561'; - constructor(private artemisTranslatePipe: ArtemisTranslatePipe) { + constructor() { super(); } diff --git a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.module.ts b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.module.ts index c9e898bcb860..d324b46c1484 100644 --- a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.module.ts +++ b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.module.ts @@ -5,8 +5,7 @@ import { ArtemisAssessmentSharedModule } from 'app/assessment/assessment-shared. import { ArtemisModelingEditorModule } from 'app/exercises/modeling/shared/modeling-editor.module'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisAssessmentSharedModule, ArtemisModelingEditorModule], - declarations: [ModelingAssessmentComponent], + imports: [ArtemisSharedModule, ArtemisAssessmentSharedModule, ArtemisModelingEditorModule, ModelingAssessmentComponent], exports: [ModelingAssessmentComponent], }) export class ModelingAssessmentModule {} diff --git a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.service.ts b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.service.ts index d03f7f22adcc..7f5c89709873 100644 --- a/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.service.ts +++ b/src/main/webapp/app/exercises/modeling/assess/modeling-assessment.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { ComplaintResponse } from 'app/entities/complaint-response.model'; @@ -12,12 +12,12 @@ type ModelingAssessmentDTO = { feedbacks: Feedback[]; assessmentNote?: string }; @Injectable({ providedIn: 'root' }) export class ModelingAssessmentService { + private http = inject(HttpClient); + private readonly MAX_FEEDBACK_TEXT_LENGTH = 500; private readonly MAX_FEEDBACK_DETAIL_TEXT_LENGTH = 5000; private resourceUrl = 'api'; - constructor(private http: HttpClient) {} - saveAssessment(resultId: number, feedbacks: Feedback[], submissionId: number, assessmentNote?: string, submit = false): Observable { let params = new HttpParams(); if (submit) { diff --git a/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.component.ts index 27d7cd03894d..1533b9407188 100644 --- a/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.component.ts @@ -14,6 +14,7 @@ import { ModelingAssessmentService } from 'app/exercises/modeling/assess/modelin import { ModelingSubmission } from 'app/entities/modeling-submission.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ModelingAssessmentComponent } from 'app/exercises/modeling/assess/modeling-assessment.component'; +import { UnreferencedFeedbackComponent } from 'app/exercises/shared/unreferenced-feedback/unreferenced-feedback.component'; import { catchError, concatMap, map, tap } from 'rxjs/operators'; import { getLatestSubmissionResult, setLatestSubmissionResult } from 'app/entities/submission.model'; import { getPositiveAndCappedTotalScore, getTotalMaxPoints } from 'app/exercises/shared/exercise/exercise.utils'; @@ -28,13 +29,39 @@ import { forkJoin } from 'rxjs'; import { filterInvalidFeedback } from 'app/exercises/modeling/assess/modeling-assessment.util'; import { Theme, ThemeService } from 'app/core/theme/theme.service'; import { scrollToTopOfPage } from 'app/shared/util/utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { FormsModule } from '@angular/forms'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { CollapsableAssessmentInstructionsComponent } from 'app/assessment/assessment-instructions/collapsable-assessment-instructions/collapsable-assessment-instructions.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-example-modeling-submission', templateUrl: './example-modeling-submission.component.html', styleUrls: ['./example-modeling-submission.component.scss'], + imports: [ + TranslateDirective, + HelpIconComponent, + FormsModule, + FaIconComponent, + ModelingEditorComponent, + ModelingAssessmentComponent, + UnreferencedFeedbackComponent, + CollapsableAssessmentInstructionsComponent, + ArtemisTranslatePipe, + ], }) export class ExampleModelingSubmissionComponent implements OnInit, FeedbackMarker { + private exerciseService = inject(ExerciseService); + private exampleSubmissionService = inject(ExampleSubmissionService); + private modelingAssessmentService = inject(ModelingAssessmentService); + private tutorParticipationService = inject(TutorParticipationService); + private alertService = inject(AlertService); + private route = inject(ActivatedRoute); + private router = inject(Router); + private navigationUtilService = inject(ArtemisNavigationUtilService); + @ViewChild(ModelingEditorComponent, { static: false }) modelingEditor: ModelingEditorComponent; @ViewChild(ModelingAssessmentComponent, { static: false }) @@ -106,16 +133,7 @@ export class ExampleModelingSubmissionComponent implements OnInit, FeedbackMarke faCodeBranch = faCodeBranch; faChalkboardTeacher = faChalkboardTeacher; - constructor( - private exerciseService: ExerciseService, - private exampleSubmissionService: ExampleSubmissionService, - private modelingAssessmentService: ModelingAssessmentService, - private tutorParticipationService: TutorParticipationService, - private alertService: AlertService, - private route: ActivatedRoute, - private router: Router, - private navigationUtilService: ArtemisNavigationUtilService, - ) { + constructor() { effect(() => { // Update highlighted elements as soon as current theme changes const highlightColor = this.highlightColor(); diff --git a/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.module.ts b/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.module.ts index b3dca289b27a..6d6b50342419 100644 --- a/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.module.ts +++ b/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.module.ts @@ -19,7 +19,7 @@ import { ArtemisSharedComponentModule } from 'app/shared/components/shared-compo ArtemisAssessmentSharedModule, AssessmentInstructionsModule, ArtemisSharedComponentModule, + ExampleModelingSubmissionComponent, ], - declarations: [ExampleModelingSubmissionComponent], }) export class ArtemisExampleModelingSubmissionModule {} diff --git a/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.route.ts b/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.route.ts index c1bee6307701..387a5d3ec926 100644 --- a/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.route.ts +++ b/src/main/webapp/app/exercises/modeling/manage/example-modeling/example-modeling-submission.route.ts @@ -1,13 +1,13 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { ExampleModelingSubmissionComponent } from './example-modeling-submission.component'; + import { Authority } from 'app/shared/constants/authority.constants'; export const routes: Routes = [ { path: ':courseId/modeling-exercises/:exerciseId/example-submissions/:exampleSubmissionId', - component: ExampleModelingSubmissionComponent, + loadComponent: () => import('./example-modeling-submission.component').then((m) => m.ExampleModelingSubmissionComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.exampleSubmission.home.editor', diff --git a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-detail.component.ts b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-detail.component.ts index 487d9213e763..42c7363b1c28 100644 --- a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-detail.component.ts +++ b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-detail.component.ts @@ -1,8 +1,10 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { SafeHtml } from '@angular/platform-browser'; import { ActivatedRoute } from '@angular/router'; import { HttpResponse } from '@angular/common/http'; import { UMLModel } from '@ls1intum/apollon'; +import { NonProgrammingExerciseDetailCommonActionsComponent } from 'app/exercises/shared/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.component'; +import { ExerciseDetailStatisticsComponent } from 'app/exercises/shared/statistics/exercise-detail-statistics.component'; import { Subscription } from 'rxjs'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ModelingExerciseService } from './modeling-exercise.service'; @@ -25,12 +27,25 @@ import { getExerciseProblemDetailSection, } from 'app/exercises/shared/utils'; import { DetailOverviewSection, DetailType } from 'app/detail-overview-list/detail-overview-list.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { DetailOverviewListComponent } from 'app/detail-overview-list/detail-overview-list.component'; @Component({ selector: 'jhi-modeling-exercise-detail', templateUrl: './modeling-exercise-detail.component.html', + imports: [TranslateDirective, DocumentationButtonComponent, NonProgrammingExerciseDetailCommonActionsComponent, ExerciseDetailStatisticsComponent, DetailOverviewListComponent], }) export class ModelingExerciseDetailComponent implements OnInit, OnDestroy { + private eventManager = inject(EventManager); + private modelingExerciseService = inject(ModelingExerciseService); + private route = inject(ActivatedRoute); + private artemisMarkdown = inject(ArtemisMarkdownService); + private alertService = inject(AlertService); + private statisticsService = inject(StatisticsService); + private accountService = inject(AccountService); + private profileService = inject(ProfileService); + readonly documentationType: DocumentationType = 'Model'; readonly ExerciseType = ExerciseType; readonly dayjs = dayjs; @@ -52,17 +67,6 @@ export class ModelingExerciseDetailComponent implements OnInit, OnDestroy { isAdmin = false; isApollonProfileActive = false; - constructor( - private eventManager: EventManager, - private modelingExerciseService: ModelingExerciseService, - private route: ActivatedRoute, - private artemisMarkdown: ArtemisMarkdownService, - private alertService: AlertService, - private statisticsService: StatisticsService, - private accountService: AccountService, - private profileService: ProfileService, - ) {} - ngOnInit() { this.isAdmin = this.accountService.isAdmin(); this.subscription = this.route.params.subscribe((params) => { diff --git a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-paging.service.ts b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-paging.service.ts index d7038c75bad7..33b28ca196dd 100644 --- a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-paging.service.ts +++ b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-paging.service.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ExercisePagingService } from 'app/exercises/shared/manage/exercise-paging.service'; @@ -7,7 +7,9 @@ import { ExercisePagingService } from 'app/exercises/shared/manage/exercise-pagi export class ModelingExercisePagingService extends ExercisePagingService { private static readonly RESOURCE_URL = 'api/modeling-exercises'; - constructor(http: HttpClient) { + constructor() { + const http = inject(HttpClient); + super(http, ModelingExercisePagingService.RESOURCE_URL); } } diff --git a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-resolver.service.ts b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-resolver.service.ts index 3744c119d2bc..7fa79cb8a351 100644 --- a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-resolver.service.ts +++ b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-resolver.service.ts @@ -5,7 +5,7 @@ import { ExerciseGroupService } from 'app/exam/manage/exercise-groups/exercise-g import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { Course } from 'app/entities/course.model'; import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; import { filter, map } from 'rxjs/operators'; import { of } from 'rxjs'; @@ -13,11 +13,9 @@ import { UMLDiagramType } from '@ls1intum/apollon'; @Injectable({ providedIn: 'root' }) export class ModelingExerciseResolver implements Resolve { - constructor( - private modelingExerciseService: ModelingExerciseService, - private courseService: CourseManagementService, - private exerciseGroupService: ExerciseGroupService, - ) {} + private modelingExerciseService = inject(ModelingExerciseService); + private courseService = inject(CourseManagementService); + private exerciseGroupService = inject(ExerciseGroupService); resolve(route: ActivatedRouteSnapshot) { if (route.params['exerciseId']) { diff --git a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-update.component.ts b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-update.component.ts index 18faee221f26..e5fb402000db 100644 --- a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-update.component.ts +++ b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise-update.component.ts @@ -1,7 +1,12 @@ -import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { HttpErrorResponse } from '@angular/common/http'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; +import { DifficultyPickerComponent } from 'app/exercises/shared/difficulty-picker/difficulty-picker.component'; +import { ExerciseFeedbackSuggestionOptionsComponent } from 'app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.component'; +import { IncludedInOverallScorePickerComponent } from 'app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.component'; +import { PresentationScoreComponent } from 'app/exercises/shared/presentation-score/presentation-score.component'; +import { GradingInstructionsDetailsComponent } from 'app/exercises/shared/structured-grading-criterion/grading-instructions-details/grading-instructions-details.component'; import { ModelingExerciseService } from './modeling-exercise.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; @@ -26,23 +31,69 @@ import { loadCourseExerciseCategories } from 'app/exercises/shared/course-exerci import { FormSectionStatus } from 'app/forms/form-status-bar/form-status-bar.component'; import { Subscription } from 'rxjs'; import { ExerciseTitleChannelNameComponent } from 'app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component'; -import { NgModel } from '@angular/forms'; +import { FormsModule, NgModel } from '@angular/forms'; import { ExerciseUpdatePlagiarismComponent } from 'app/exercises/shared/plagiarism/exercise-update-plagiarism/exercise-update-plagiarism.component'; import { TeamConfigFormGroupComponent } from 'app/exercises/shared/team-config-form-group/team-config-form-group.component'; import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; import { FormulaAction } from 'app/shared/monaco-editor/model/actions/formula.action'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { FormStatusBarComponent } from 'app/forms/form-status-bar/form-status-bar.component'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { CategorySelectorComponent } from 'app/shared/category-selector/category-selector.component'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { CompetencySelectionComponent } from 'app/shared/competency-selection/competency-selection.component'; +import { CustomMinDirective } from 'app/shared/validators/custom-min-validator.directive'; +import { CustomMaxDirective } from 'app/shared/validators/custom-max-validator.directive'; +import { FormFooterComponent } from 'app/forms/form-footer/form-footer.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-modeling-exercise-update', templateUrl: './modeling-exercise-update.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FormsModule, + TranslateDirective, + DocumentationButtonComponent, + FormStatusBarComponent, + ExerciseTitleChannelNameComponent, + HelpIconComponent, + CategorySelectorComponent, + DifficultyPickerComponent, + TeamConfigFormGroupComponent, + MarkdownEditorMonacoComponent, + CompetencySelectionComponent, + ModelingEditorComponent, + FormDateTimePickerComponent, + IncludedInOverallScorePickerComponent, + CustomMinDirective, + CustomMaxDirective, + ExerciseFeedbackSuggestionOptionsComponent, + ExerciseUpdatePlagiarismComponent, + PresentationScoreComponent, + GradingInstructionsDetailsComponent, + FormFooterComponent, + ArtemisTranslatePipe, + ], }) export class ModelingExerciseUpdateComponent implements AfterViewInit, OnDestroy, OnInit { + private alertService = inject(AlertService); + private modelingExerciseService = inject(ModelingExerciseService); + private modalService = inject(NgbModal); + private popupService = inject(ExerciseUpdateWarningService); + private courseService = inject(CourseManagementService); + private exerciseService = inject(ExerciseService); + private exerciseGroupService = inject(ExerciseGroupService); + private eventManager = inject(EventManager); + private activatedRoute = inject(ActivatedRoute); + private navigationUtilService = inject(ArtemisNavigationUtilService); + private changeDetectorRef = inject(ChangeDetectorRef); + @ViewChild(ExerciseTitleChannelNameComponent) exerciseTitleChannelNameComponent: ExerciseTitleChannelNameComponent; @ViewChild(ExerciseUpdatePlagiarismComponent) exerciseUpdatePlagiarismComponent?: ExerciseUpdatePlagiarismComponent; @ViewChild(TeamConfigFormGroupComponent) teamConfigFormGroupComponent?: TeamConfigFormGroupComponent; - @ViewChild(ModelingEditorComponent, { static: false }) - modelingEditor?: ModelingEditorComponent; + @ViewChild(ModelingEditorComponent, { static: false }) modelingEditor?: ModelingEditorComponent; @ViewChild('bonusPoints') bonusPoints?: NgModel; @ViewChild('points') points?: NgModel; @ViewChild('solutionPublicationDate') solutionPublicationDateField?: FormDateTimePickerComponent; @@ -80,20 +131,6 @@ export class ModelingExerciseUpdateComponent implements AfterViewInit, OnDestroy plagiarismSubscription?: Subscription; teamSubscription?: Subscription; - constructor( - private alertService: AlertService, - private modelingExerciseService: ModelingExerciseService, - private modalService: NgbModal, - private popupService: ExerciseUpdateWarningService, - private courseService: CourseManagementService, - private exerciseService: ExerciseService, - private exerciseGroupService: ExerciseGroupService, - private eventManager: EventManager, - private activatedRoute: ActivatedRoute, - private navigationUtilService: ArtemisNavigationUtilService, - private changeDetectorRef: ChangeDetectorRef, - ) {} - get editType(): EditType { if (this.isImport) { return EditType.IMPORT; diff --git a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.component.ts b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.component.ts index e1ea83ce136e..070f9134faf9 100644 --- a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.component.ts +++ b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.component.ts @@ -11,10 +11,20 @@ import { AlertService } from 'app/core/util/alert.service'; import { faBook, faPlus, faSort, faTable, faTimes, faTrash, faUsers, faWrench } from '@fortawesome/free-solid-svg-icons'; import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { CourseExerciseService } from 'app/exercises/shared/course-exercises/course-exercise.service'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { FormsModule } from '@angular/forms'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { RouterLink } from '@angular/router'; +import { ExerciseCategoriesComponent } from 'app/shared/exercise-categories/exercise-categories.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-modeling-exercise', templateUrl: './modeling-exercise.component.html', + imports: [SortDirective, FormsModule, SortByDirective, TranslateDirective, FaIconComponent, RouterLink, ExerciseCategoriesComponent, DeleteButtonDirective, ArtemisDatePipe], }) export class ModelingExerciseComponent extends ExerciseComponent { protected exerciseService = inject(ExerciseService); diff --git a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.module.ts b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.module.ts index 752915ce1096..3e5a9684b5b4 100644 --- a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.module.ts +++ b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.module.ts @@ -26,7 +26,7 @@ import { ExerciseUpdateNotificationModule } from 'app/exercises/shared/exercise- import { ExerciseUpdatePlagiarismModule } from 'app/exercises/shared/plagiarism/exercise-update-plagiarism/exercise-update-plagiarism.module'; import { DetailModule } from 'app/detail-overview-list/detail.module'; import { ArtemisExerciseModule } from 'app/exercises/shared/exercise/exercise.module'; -import { FormsModule } from 'app/forms/forms.module'; +import { ArtemisFormsModule } from 'app/forms/artemis-forms.module'; import { ExerciseFeedbackSuggestionOptionsModule } from 'app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.module'; @NgModule({ @@ -56,9 +56,11 @@ import { ExerciseFeedbackSuggestionOptionsModule } from 'app/exercises/shared/fe ExerciseUpdatePlagiarismModule, ExerciseFeedbackSuggestionOptionsModule, DetailModule, - FormsModule, + ArtemisFormsModule, + ModelingExerciseComponent, + ModelingExerciseDetailComponent, + ModelingExerciseUpdateComponent, ], - declarations: [ModelingExerciseComponent, ModelingExerciseDetailComponent, ModelingExerciseUpdateComponent], exports: [ModelingExerciseComponent], }) export class ArtemisModelingExerciseModule {} diff --git a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.route.ts b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.route.ts index 01b6d2a081bd..aae78b63a80b 100644 --- a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.route.ts +++ b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.route.ts @@ -1,18 +1,15 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { ModelingExerciseDetailComponent } from './modeling-exercise-detail.component'; -import { ModelingExerciseUpdateComponent } from 'app/exercises/modeling/manage/modeling-exercise-update.component'; -import { PlagiarismInspectorComponent } from 'app/exercises/shared/plagiarism/plagiarism-inspector/plagiarism-inspector.component'; + import { Authority } from 'app/shared/constants/authority.constants'; -import { ExerciseStatisticsComponent } from 'app/exercises/shared/statistics/exercise-statistics.component'; -import { ExampleSubmissionsComponent } from 'app/exercises/shared/example-submission/example-submissions.component'; + import { ModelingExerciseResolver } from 'app/exercises/modeling/manage/modeling-exercise-resolver.service'; export const routes: Routes = [ { path: ':courseId/modeling-exercises/new', - component: ModelingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/modeling/manage/modeling-exercise-update.component').then((m) => m.ModelingExerciseUpdateComponent), resolve: { modelingExercise: ModelingExerciseResolver, }, @@ -24,7 +21,7 @@ export const routes: Routes = [ }, { path: ':courseId/modeling-exercises/:exerciseId/edit', - component: ModelingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/modeling/manage/modeling-exercise-update.component').then((m) => m.ModelingExerciseUpdateComponent), resolve: { modelingExercise: ModelingExerciseResolver, }, @@ -36,7 +33,7 @@ export const routes: Routes = [ }, { path: ':courseId/modeling-exercises/:exerciseId/import', - component: ModelingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/modeling/manage/modeling-exercise-update.component').then((m) => m.ModelingExerciseUpdateComponent), resolve: { modelingExercise: ModelingExerciseResolver, }, @@ -48,7 +45,7 @@ export const routes: Routes = [ }, { path: ':courseId/modeling-exercises/:exerciseId', - component: ModelingExerciseDetailComponent, + loadComponent: () => import('./modeling-exercise-detail.component').then((m) => m.ModelingExerciseDetailComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.modelingExercise.home.title', @@ -57,7 +54,7 @@ export const routes: Routes = [ }, { path: ':courseId/modeling-exercises/:exerciseId/example-submissions', - component: ExampleSubmissionsComponent, + loadComponent: () => import('app/exercises/shared/example-submission/example-submissions.component').then((m) => m.ExampleSubmissionsComponent), resolve: { exercise: ModelingExerciseResolver, }, @@ -69,7 +66,7 @@ export const routes: Routes = [ }, { path: ':courseId/modeling-exercises/:exerciseId/plagiarism', - component: PlagiarismInspectorComponent, + loadComponent: () => import('app/exercises/shared/plagiarism/plagiarism-inspector/plagiarism-inspector.component').then((m) => m.PlagiarismInspectorComponent), resolve: { exercise: ModelingExerciseResolver, }, @@ -85,7 +82,7 @@ export const routes: Routes = [ }, { path: ':courseId/modeling-exercises/:exerciseId/exercise-statistics', - component: ExerciseStatisticsComponent, + loadComponent: () => import('app/exercises/shared/statistics/exercise-statistics.component').then((m) => m.ExerciseStatisticsComponent), resolve: { exercise: ModelingExerciseResolver, }, diff --git a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.service.ts b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.service.ts index a56210f6e655..da4833981af3 100644 --- a/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.service.ts +++ b/src/main/webapp/app/exercises/modeling/manage/modeling-exercise.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; @@ -15,13 +15,15 @@ export type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class ModelingExerciseService implements ExerciseServicable { + private http = inject(HttpClient); + private exerciseService = inject(ExerciseService); + public resourceUrl = 'api/modeling-exercises'; public adminResourceUrl = 'api/admin/modeling-exercises'; - constructor( - private http: HttpClient, - private exerciseService: ExerciseService, - ) { + constructor() { + const exerciseService = this.exerciseService; + this.exerciseService = exerciseService; } diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-participation.module.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-participation.module.ts index f558adc3103e..be5a3969b048 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-participation.module.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-participation.module.ts @@ -8,7 +8,7 @@ import { ArtemisModelingEditorModule } from 'app/exercises/modeling/shared/model import { ModelingAssessmentModule } from 'app/exercises/modeling/assess/modeling-assessment.module'; import { ArtemisModelingParticipationRoutingModule } from 'app/exercises/modeling/participate/modeling-participation.route'; import { ArtemisHeaderExercisePageWithDetailsModule } from 'app/exercises/shared/exercise-headers/exercise-headers.module'; -import { ArtemisTeamSubmissionSyncModule } from 'app/exercises/shared/team-submission-sync/team-submission-sync.module'; + import { ArtemisFullscreenModule } from 'app/shared/fullscreen/fullscreen.module'; import { RatingModule } from 'app/exercises/shared/rating/rating.module'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; @@ -25,14 +25,13 @@ import { RequestFeedbackButtonComponent } from 'app/overview/exercise-details/re ArtemisModelingParticipationRoutingModule, ArtemisHeaderExercisePageWithDetailsModule, ModelingAssessmentModule, - ArtemisTeamSubmissionSyncModule, ArtemisFullscreenModule, RatingModule, ArtemisMarkdownModule, ArtemisTeamParticipeModule, RequestFeedbackButtonComponent, + ModelingSubmissionComponent, ], - declarations: [ModelingSubmissionComponent], exports: [ModelingSubmissionComponent], }) export class ArtemisModelingParticipationModule {} diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-participation.route.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-participation.route.ts index 1c460eee70b0..d8cdb2396651 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-participation.route.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-participation.route.ts @@ -2,13 +2,13 @@ import { NgModule } from '@angular/core'; import { PendingChangesGuard } from 'app/shared/guard/pending-changes.guard'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { RouterModule, Routes } from '@angular/router'; -import { ModelingSubmissionComponent } from './modeling-submission.component'; + import { Authority } from 'app/shared/constants/authority.constants'; export const routes: Routes = [ { path: 'participate/:participationId', - component: ModelingSubmissionComponent, + loadComponent: () => import('./modeling-submission.component').then((m) => m.ModelingSubmissionComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.modelingExercise.home.title', @@ -18,7 +18,7 @@ export const routes: Routes = [ }, { path: 'participate/:participationId/submission/:submissionId', - component: ModelingSubmissionComponent, + loadComponent: () => import('./modeling-submission.component').then((m) => m.ModelingSubmissionComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.modelingExercise.home.title', diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index b77b05f098dd..a896d312a93e 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -1,8 +1,7 @@ import { HttpErrorResponse } from '@angular/common/http'; -import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, Input, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { Patch, Selection, UMLDiagramType, UMLElementType, UMLModel, UMLRelationshipType } from '@ls1intum/apollon'; -import { TranslateService } from '@ngx-translate/core'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { ComplaintType } from 'app/entities/complaint.model'; import { Feedback, buildFeedbackTextForReview, checkSubsequentFeedbackInAssessment } from 'app/entities/feedback.model'; @@ -14,9 +13,13 @@ import { getFirstResultWithComplaint, getLatestSubmissionResult } from 'app/enti import { ModelingAssessmentService } from 'app/exercises/modeling/assess/modeling-assessment.service'; import { ModelingSubmissionService } from 'app/exercises/modeling/participate/modeling-submission.service'; import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; +import { HeaderParticipationPageComponent } from 'app/exercises/shared/exercise-headers/header-participation-page.component'; import { getExerciseDueDate, hasExerciseDueDatePassed } from 'app/exercises/shared/exercise/exercise.utils'; +import { RatingComponent } from 'app/exercises/shared/rating/rating.component'; import { addParticipationToResult, getUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; import { AccountService } from 'app/core/auth/account.service'; +import { TeamSubmissionSyncComponent } from 'app/exercises/shared/team-submission-sync/team-submission-sync.component'; +import { TeamParticipateInfoBoxComponent } from 'app/exercises/shared/team/team-participate/team-participate-info-box.component'; import { GuidedTourService } from 'app/guided-tour/guided-tour.service'; import { modelingTour } from 'app/guided-tour/tours/modeling-tour'; import { ParticipationWebsocketService } from 'app/overview/participation-websocket.service'; @@ -39,29 +42,74 @@ import { AssessmentType } from 'app/entities/assessment-type.model'; import { catchError, filter, skip, switchMap, tap } from 'rxjs/operators'; import { onError } from 'app/shared/util/global.utils'; import { of } from 'rxjs'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { RequestFeedbackButtonComponent } from 'app/overview/exercise-details/request-feedback-button/request-feedback-button.component'; +import { ResultHistoryComponent } from 'app/overview/result-history/result-history.component'; +import { ResizeableContainerComponent } from 'app/shared/resizeable-container/resizeable-container.component'; +import { FullscreenComponent } from 'app/shared/fullscreen/fullscreen.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ModelingAssessmentComponent } from '../assess/modeling-assessment.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DecimalPipe, NgClass } from '@angular/common'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { AdditionalFeedbackComponent } from 'app/shared/additional-feedback/additional-feedback.component'; +import { ComplaintsStudentViewComponent } from 'app/complaints/complaints-for-students/complaints-student-view.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-modeling-submission', templateUrl: './modeling-submission.component.html', styleUrls: ['./modeling-submission.component.scss'], + imports: [ + HeaderParticipationPageComponent, + ButtonComponent, + RouterLink, + RequestFeedbackButtonComponent, + ResultHistoryComponent, + ResizeableContainerComponent, + TeamParticipateInfoBoxComponent, + FullscreenComponent, + ModelingEditorComponent, + FaIconComponent, + TeamSubmissionSyncComponent, + ModelingAssessmentComponent, + TranslateDirective, + NgClass, + NgbTooltip, + AdditionalFeedbackComponent, + RatingComponent, + ComplaintsStudentViewComponent, + DecimalPipe, + ArtemisTranslatePipe, + HtmlForMarkdownPipe, + ], }) export class ModelingSubmissionComponent implements OnInit, OnDestroy, ComponentCanDeactivate { + private jhiWebsocketService = inject(JhiWebsocketService); + private modelingSubmissionService = inject(ModelingSubmissionService); + private modelingAssessmentService = inject(ModelingAssessmentService); + private alertService = inject(AlertService); + private route = inject(ActivatedRoute); + private participationWebsocketService = inject(ParticipationWebsocketService); + private guidedTourService = inject(GuidedTourService); + private accountService = inject(AccountService); + readonly addParticipationToResult = addParticipationToResult; readonly buildFeedbackTextForReview = buildFeedbackTextForReview; + readonly ButtonType = ButtonType; - @ViewChild(ModelingEditorComponent, { static: false }) - modelingEditor: ModelingEditorComponent; - ButtonType = ButtonType; + @ViewChild(ModelingEditorComponent, { static: false }) modelingEditor: ModelingEditorComponent; @Input() participationId?: number; - @Input() displayHeader: boolean = true; - @Input() isPrinting?: boolean = false; - @Input() expandProblemStatement?: boolean = false; - @Input() inputExercise?: ModelingExercise; @Input() inputSubmission?: ModelingSubmission; @Input() inputParticipation?: StudentParticipation; + @Input() isExamSummary = false; + @Input() displayHeader = true; + @Input() isPrinting = false; + @Input() expandProblemStatement = false; private subscription: Subscription; private manualResultUpdateListener: Subscription; @@ -91,11 +139,11 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component umlModel: UMLModel; // input model for Apollon hasElements = false; // indicates if the current model has at least one element - isSaving: boolean; + isSaving = false; isChanged: boolean; retryStarted = false; autoSaveInterval: number; - autoSaveTimer: number; + autoSaveTimer = 0; explanation: string; // current explanation on text editor @@ -103,7 +151,7 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component // indicates if the assessment due date is in the past. the assessment will not be loaded and displayed to the student if it is not. isAfterAssessmentDueDate: boolean; - isLoading: boolean; + isLoading = true; isLate: boolean; // indicates if the submission is late isGeneratingFeedback: boolean; ComplaintType = ComplaintType; @@ -111,8 +159,8 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component // submission sync with team members private submissionChange = new Subject(); - submissionObservable = this.submissionChange.asObservable(); - submissionPatchObservable = new Subject(); + protected submissionObservable = this.submissionChange.asObservable(); + protected submissionPatchObservable = new Subject(); // private modelingEditorInitialized = new ReplaySubject(); resizeOptions = { verticalResize: true }; @@ -124,24 +172,8 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component faTimeline = faTimeline; // mode - isFeedbackView: boolean = false; - showResultHistory: boolean = false; - - constructor( - private jhiWebsocketService: JhiWebsocketService, - private modelingSubmissionService: ModelingSubmissionService, - private modelingAssessmentService: ModelingAssessmentService, - private alertService: AlertService, - private route: ActivatedRoute, - private translateService: TranslateService, - private participationWebsocketService: ParticipationWebsocketService, - private guidedTourService: GuidedTourService, - private accountService: AccountService, - ) { - this.isSaving = false; - this.autoSaveTimer = 0; - this.isLoading = true; - } + isFeedbackView = false; + showResultHistory = false; ngOnInit(): void { if (this.inputValuesArePresent()) { @@ -255,11 +287,7 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component this.participation = this.inputParticipation; } - if (this.submission.model) { - this.umlModel = JSON.parse(this.submission.model); - this.hasElements = this.umlModel.elements && Object.values(this.umlModel.elements).length !== 0; - } - this.explanation = this.submission.explanationText ?? ''; + this.updateModelAndExplanation(); } /** @@ -314,11 +342,9 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component dayjs(this.participation.initializationDate).isAfter(getExerciseDueDate(this.modelingExercise, this.participation)); this.isAfterAssessmentDueDate = !this.modelingExercise.assessmentDueDate || dayjs().isAfter(this.modelingExercise.assessmentDueDate); - if (this.submission.model) { - this.umlModel = JSON.parse(this.submission.model); - this.hasElements = this.umlModel.elements && Object.values(this.umlModel.elements).length !== 0; - } - this.explanation = this.submission.explanationText ?? ''; + + this.updateModelAndExplanation(); + this.subscribeToWebsockets(); if ((getLatestSubmissionResult(this.submission) && this.isAfterAssessmentDueDate) || this.isFeedbackView) { this.result = getLatestSubmissionResult(this.submission); @@ -342,6 +368,14 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component this.guidedTourService.enableTourForExercise(this.modelingExercise, modelingTour, true); } + private updateModelAndExplanation(): void { + if (this.submission.model) { + this.umlModel = JSON.parse(this.submission.model); + this.hasElements = this.umlModel.elements && Object.values(this.umlModel.elements).length !== 0; + } + this.explanation = this.submission.explanationText ?? ''; + } + /** * If the submission is submitted, subscribe to new results for the participation. * Otherwise, subscribe to the automatic submission (which happens when the submission is un-submitted and the exercise due date is over). diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.service.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.service.ts index 9da063181dbe..2fb2188d001c 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.service.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -12,12 +12,10 @@ export type EntityResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class ModelingSubmissionService { - public resourceUrl = 'api'; + private http = inject(HttpClient); + private submissionService = inject(SubmissionService); - constructor( - private http: HttpClient, - private submissionService: SubmissionService, - ) {} + public resourceUrl = 'api'; /** * Create a new modeling submission diff --git a/src/main/webapp/app/exercises/modeling/shared/modeling-editor.component.ts b/src/main/webapp/app/exercises/modeling/shared/modeling-editor.component.ts index f9e488df0298..09c5b76ce6b3 100644 --- a/src/main/webapp/app/exercises/modeling/shared/modeling-editor.component.ts +++ b/src/main/webapp/app/exercises/modeling/shared/modeling-editor.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { AfterViewInit, Component, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges, ViewEncapsulation, inject } from '@angular/core'; import { ApollonEditor, ApollonMode, SVG, UMLDiagramType, UMLElementType, UMLModel, UMLRelationship, UMLRelationshipType } from '@ls1intum/apollon'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { associationUML, personUML, studentUML } from 'app/guided-tour/guided-tour-task.model'; @@ -9,14 +9,23 @@ import { faQuestionCircle } from '@fortawesome/free-regular-svg-icons'; import { ModelingComponent } from 'app/exercises/modeling/shared/modeling.component'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { Patch } from '@ls1intum/apollon'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass, NgStyle } from '@angular/common'; +import { ModelingExplanationEditorComponent } from './modeling-explanation-editor.component'; @Component({ selector: 'jhi-modeling-editor', templateUrl: './modeling-editor.component.html', styleUrls: ['./modeling-editor.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [TranslateDirective, FaIconComponent, NgStyle, NgClass, ModelingExplanationEditorComponent], }) export class ModelingEditorComponent extends ModelingComponent implements AfterViewInit, OnDestroy, OnChanges { + private modalService = inject(NgbModal); + private guidedTourService = inject(GuidedTourService); + private sanitizer = inject(DomSanitizer); + @Input() showHelpButton = true; @Input() withExplanation = false; @Input() savedStatus?: { isChanged?: boolean; isSaving?: boolean }; @@ -42,11 +51,7 @@ export class ModelingEditorComponent extends ModelingComponent implements AfterV readonlyApollonDiagram?: SVG; readOnlySVG?: SafeHtml; - constructor( - private modalService: NgbModal, - private guidedTourService: GuidedTourService, - private sanitizer: DomSanitizer, - ) { + constructor() { super(); } diff --git a/src/main/webapp/app/exercises/modeling/shared/modeling-editor.module.ts b/src/main/webapp/app/exercises/modeling/shared/modeling-editor.module.ts index 832db40470c3..6567fa4b7efb 100644 --- a/src/main/webapp/app/exercises/modeling/shared/modeling-editor.module.ts +++ b/src/main/webapp/app/exercises/modeling/shared/modeling-editor.module.ts @@ -4,8 +4,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ModelingExplanationEditorComponent } from './modeling-explanation-editor.component'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [ModelingEditorComponent, ModelingExplanationEditorComponent], + imports: [ArtemisSharedModule, ModelingEditorComponent, ModelingExplanationEditorComponent], exports: [ModelingEditorComponent, ModelingExplanationEditorComponent], }) export class ArtemisModelingEditorModule {} diff --git a/src/main/webapp/app/exercises/modeling/shared/modeling-explanation-editor.component.ts b/src/main/webapp/app/exercises/modeling/shared/modeling-explanation-editor.component.ts index 3319f4d2b399..079c59238e8b 100644 --- a/src/main/webapp/app/exercises/modeling/shared/modeling-explanation-editor.component.ts +++ b/src/main/webapp/app/exercises/modeling/shared/modeling-explanation-editor.component.ts @@ -1,33 +1,24 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { MAX_SUBMISSION_TEXT_LENGTH } from 'app/shared/constants/input.constants'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { onTextEditorTab } from 'app/utils/text.utils'; @Component({ selector: 'jhi-modeling-explanation-editor', templateUrl: './modeling-explanation-editor.component.html', styleUrls: ['./modeling-explanation-editor.component.scss'], + imports: [TranslateDirective, FormsModule], }) export class ModelingExplanationEditorComponent { - @Input() - readOnly = false; - - @Input() - explanation: string; - - @Output() - explanationChange = new EventEmitter(); + @Input() readOnly = false; + @Input() explanation: string; + @Output() explanationChange = new EventEmitter(); readonly maxCharacterCount = MAX_SUBMISSION_TEXT_LENGTH; - // Add tab to the value of textarea instead of moving to the next element in DOM - onTextEditorTab(editor: HTMLTextAreaElement, event: Event) { - event.preventDefault(); - const value = editor.value; - const start = editor.selectionStart; - const end = editor.selectionEnd; - - editor.value = value.substring(0, start) + '\t' + value.substring(end); - editor.selectionStart = editor.selectionEnd = start + 1; - } + // used in the html template + protected readonly onTextEditorTab = onTextEditorTab; onExplanationInput(newValue: string) { this.explanationChange.emit(newValue); diff --git a/src/main/webapp/app/exercises/modeling/shared/modeling.component.ts b/src/main/webapp/app/exercises/modeling/shared/modeling.component.ts index 51d061c46cdc..667f25718fc6 100644 --- a/src/main/webapp/app/exercises/modeling/shared/modeling.component.ts +++ b/src/main/webapp/app/exercises/modeling/shared/modeling.component.ts @@ -4,7 +4,9 @@ import { ApollonEditor, UMLDiagramType, UMLModel } from '@ls1intum/apollon'; import { MODELING_EDITOR_MAX_HEIGHT, MODELING_EDITOR_MAX_WIDTH, MODELING_EDITOR_MIN_HEIGHT, MODELING_EDITOR_MIN_WIDTH } from 'app/shared/constants/modeling.constants'; import interact from 'interactjs'; -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class ModelingComponent { @ViewChild('editorContainer', { static: false }) editorContainer: ElementRef; @ViewChild('resizeContainer', { static: false }) resizeContainer: ElementRef; @@ -20,8 +22,6 @@ export abstract class ModelingComponent { faGripLines = faGripLines; faGripLinesVertical = faGripLinesVertical; - protected constructor() {} - protected setupInteract(): void { if (this.resizeOptions) { interact('.resizable') diff --git a/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-container.component.html b/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-container.component.html index 9382ac324adf..0ce53d07531a 100644 --- a/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-container.component.html +++ b/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-container.component.html @@ -52,35 +52,36 @@ - - @if (!loadingParticipation && !participationCouldNotBeFetched) { - -
    -
    - -
    -
    - } -
    + + + + + + + + @if (!loadingParticipation && !participationCouldNotBeFetched) { + +
    +
    + +
    +
    + } + @@ -97,28 +98,29 @@ - - @if (isAtLeastEditor && localVCEnabled && !isTestRun) { - - - - - } - - @if (isAtLeastEditor && !localVCEnabled) { - - } - + + + @if (isAtLeastEditor && localVCEnabled && !isTestRun) { + + + + + } + + @if (isAtLeastEditor && !localVCEnabled) { + + } + @if (participation) { ; - @ContentChild('overrideExportGoToRepository') overrideExportGoToRepository: TemplateRef; + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // @ContentChild('overrideCodeEditor') overrideCodeEditor: TemplateRef; + // @ContentChild('overrideExportGoToRepository') overrideExportGoToRepository: TemplateRef; // listener, will get notified upon loading of feedback @Output() onFeedbackLoaded = new EventEmitter(); // function override, if set will be executed instead of going to the next submission page @@ -122,24 +162,9 @@ export class CodeEditorTutorAssessmentContainerComponent implements OnInit, OnDe return this.feedbackSuggestions.filter((feedback) => !feedback.reference); } - constructor( - private manualResultService: ProgrammingAssessmentManualResultService, - private router: Router, - private location: Location, - private accountService: AccountService, - private programmingSubmissionService: ProgrammingSubmissionService, - private domainService: DomainService, - private complaintService: ComplaintService, - private route: ActivatedRoute, - private alertService: AlertService, - private structuredGradingCriterionService: StructuredGradingCriterionService, - private repositoryFileService: CodeEditorRepositoryFileService, - private programmingExerciseService: ProgrammingExerciseService, - private profileService: ProfileService, - private modalService: NgbModal, - private athenaService: AthenaService, - translateService: TranslateService, - ) { + constructor() { + const translateService = inject(TranslateService); + translateService.get('artemisApp.assessment.messages.confirmCancel').subscribe((text) => (this.cancelConfirmationText = text)); translateService.get('artemisApp.assessment.messages.acceptComplaintWithoutMoreScore').subscribe((text) => (this.acceptComplaintWithoutMoreScoreText = text)); } diff --git a/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-inline-feedback-suggestion.component.ts b/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-inline-feedback-suggestion.component.ts index 5bef12933962..f15f909d7bff 100644 --- a/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-inline-feedback-suggestion.component.ts +++ b/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-inline-feedback-suggestion.component.ts @@ -1,13 +1,17 @@ -import { Component, ElementRef, EventEmitter, Input, Output } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, Output, inject } from '@angular/core'; import { Feedback, buildFeedbackTextForReview } from 'app/entities/feedback.model'; +import { FeedbackSuggestionBadgeComponent } from 'app/exercises/shared/feedback/feedback-suggestion-badge/feedback-suggestion-badge.component'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; import { Course } from 'app/entities/course.model'; import { faCheck, faTrash } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-code-editor-tutor-assessment-inline-feedback-suggestion', templateUrl: './code-editor-tutor-assessment-inline-feedback-suggestion.component.html', styleUrls: ['./code-editor-tutor-assessment-inline-feedback-suggestion.component.scss'], + imports: [FeedbackSuggestionBadgeComponent, TranslateDirective, FaIconComponent], }) export class CodeEditorTutorAssessmentInlineFeedbackSuggestionComponent { @Input() @@ -30,9 +34,12 @@ export class CodeEditorTutorAssessmentInlineFeedbackSuggestionComponent { faCheck = faCheck; faTrash = faTrash; - public elementRef: ElementRef; // Needed for the outer editor to access the DOM node of this component + public elementRef: ElementRef; + // Needed for the outer editor to access the DOM node of this component + + constructor() { + const elementRef = inject(ElementRef); - constructor(elementRef: ElementRef) { this.elementRef = elementRef; } } diff --git a/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-inline-feedback.component.ts b/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-inline-feedback.component.ts index 1687051aa12e..f1eae56d3400 100644 --- a/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-inline-feedback.component.ts +++ b/src/main/webapp/app/exercises/programming/assess/code-editor-tutor-assessment-inline-feedback.component.ts @@ -1,5 +1,6 @@ -import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, Output, ViewChild, inject } from '@angular/core'; import { Feedback, FeedbackType, buildFeedbackTextForReview } from 'app/entities/feedback.model'; +import { FeedbackSuggestionBadgeComponent } from 'app/exercises/shared/feedback/feedback-suggestion-badge/feedback-suggestion-badge.component'; import { ButtonSize } from 'app/shared/components/button.component'; import { cloneDeep } from 'lodash-es'; import { TranslateService } from '@ngx-translate/core'; @@ -8,12 +9,38 @@ import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; import { Course } from 'app/entities/course.model'; import { faBan, faExclamationTriangle, faPencilAlt, faQuestionCircle, faSave, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; import { Subject } from 'rxjs'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { GradingInstructionLinkIconComponent } from 'app/shared/grading-instruction-link-icon/grading-instruction-link-icon.component'; +import { FormsModule } from '@angular/forms'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { AssessmentCorrectionRoundBadgeComponent } from 'app/assessment/unreferenced-feedback-detail/assessment-correction-round-badge/assessment-correction-round-badge.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { FeedbackContentPipe } from 'app/shared/pipes/feedback-content.pipe'; +import { QuotePipe } from 'app/shared/pipes/quote.pipe'; @Component({ selector: 'jhi-code-editor-tutor-assessment-inline-feedback', templateUrl: './code-editor-tutor-assessment-inline-feedback.component.html', + imports: [ + FeedbackSuggestionBadgeComponent, + TranslateDirective, + FaIconComponent, + NgbTooltip, + GradingInstructionLinkIconComponent, + FormsModule, + DeleteButtonDirective, + AssessmentCorrectionRoundBadgeComponent, + ArtemisTranslatePipe, + FeedbackContentPipe, + QuotePipe, + ], }) export class CodeEditorTutorAssessmentInlineFeedbackComponent { + private translateService = inject(TranslateService); + structuredGradingCriterionService = inject(StructuredGradingCriterionService); + @Input() get feedback(): Feedback { return this._feedback; @@ -66,11 +93,9 @@ export class CodeEditorTutorAssessmentInlineFeedbackComponent { faTrashAlt = faTrashAlt; faExclamationTriangle = faExclamationTriangle; - constructor( - private translateService: TranslateService, - public structuredGradingCriterionService: StructuredGradingCriterionService, - elementRef: ElementRef, - ) { + constructor() { + const elementRef = inject(ElementRef); + this.elementRef = elementRef; } diff --git a/src/main/webapp/app/exercises/programming/assess/manual-result/programming-assessment-manual-result.service.ts b/src/main/webapp/app/exercises/programming/assess/manual-result/programming-assessment-manual-result.service.ts index 220f3818fddf..251aa5892a57 100644 --- a/src/main/webapp/app/exercises/programming/assess/manual-result/programming-assessment-manual-result.service.ts +++ b/src/main/webapp/app/exercises/programming/assess/manual-result/programming-assessment-manual-result.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpClient, HttpParams } from '@angular/common/http'; import { ComplaintResponse } from 'app/entities/complaint-response.model'; @@ -9,12 +9,12 @@ import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class ProgrammingAssessmentManualResultService { + private http = inject(HttpClient); + private resultService = inject(ResultService); + private resourceUrl = 'api'; + // TODO: It would be good to refactor the convertDate methods into a separate service, so that we don't have to import the result service here. - constructor( - private http: HttpClient, - private resultService: ResultService, - ) {} /** * Saves a new manual result and stores it in the server diff --git a/src/main/webapp/app/exercises/programming/assess/programming-assessment.module.ts b/src/main/webapp/app/exercises/programming/assess/programming-assessment.module.ts index 7ae991b6ac22..af0ce2f667ea 100644 --- a/src/main/webapp/app/exercises/programming/assess/programming-assessment.module.ts +++ b/src/main/webapp/app/exercises/programming/assess/programming-assessment.module.ts @@ -3,7 +3,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { FormDateTimePickerModule } from 'app/shared/date-time-picker/date-time-picker.module'; import { FormsModule } from '@angular/forms'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { ProgrammingAssessmentRepoExportButtonComponent } from 'app/exercises/programming/assess/repo-export/programming-assessment-repo-export-button.component'; import { ProgrammingAssessmentRepoExportDialogComponent } from 'app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component'; import { ArtemisProgrammingAssessmentRoutingModule } from 'app/exercises/programming/assess/programming-assessment.route'; @@ -25,7 +25,6 @@ import { ComplaintsForTutorComponent } from 'app/complaints/complaints-for-tutor ArtemisSharedComponentModule, FormDateTimePickerModule, FormsModule, - FeatureToggleModule, ComplaintsForTutorComponent, ArtemisProgrammingAssessmentRoutingModule, ArtemisAssessmentSharedModule, @@ -36,8 +35,6 @@ import { ComplaintsForTutorComponent } from 'app/complaints/complaints-for-tutor AssessmentInstructionsModule, ArtemisHeaderExercisePageWithDetailsModule, SubmissionResultStatusModule, - ], - declarations: [ ProgrammingAssessmentRepoExportButtonComponent, ProgrammingAssessmentRepoExportDialogComponent, CodeEditorTutorAssessmentContainerComponent, diff --git a/src/main/webapp/app/exercises/programming/assess/programming-manual-assessment.module.ts b/src/main/webapp/app/exercises/programming/assess/programming-manual-assessment.module.ts index c33d739891c9..d53486a4a31a 100644 --- a/src/main/webapp/app/exercises/programming/assess/programming-manual-assessment.module.ts +++ b/src/main/webapp/app/exercises/programming/assess/programming-manual-assessment.module.ts @@ -20,8 +20,9 @@ import { QuotePipe } from 'app/shared/pipes/quote.pipe'; ArtemisFeedbackModule, FeedbackContentPipe, QuotePipe, + CodeEditorTutorAssessmentInlineFeedbackComponent, + CodeEditorTutorAssessmentInlineFeedbackSuggestionComponent, ], - declarations: [CodeEditorTutorAssessmentInlineFeedbackComponent, CodeEditorTutorAssessmentInlineFeedbackSuggestionComponent], exports: [CodeEditorTutorAssessmentInlineFeedbackComponent, CodeEditorTutorAssessmentInlineFeedbackSuggestionComponent], }) export class ArtemisProgrammingManualAssessmentModule {} diff --git a/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-button.component.ts b/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-button.component.ts index 9a2b5af07fb0..38cc387c67a4 100644 --- a/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-button.component.ts +++ b/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-button.component.ts @@ -1,10 +1,11 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ProgrammingAssessmentRepoExportDialogComponent } from 'app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { faDownload } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ButtonComponent } from 'app/shared/components/button.component'; @Component({ selector: 'jhi-programming-assessment-repo-export', @@ -20,8 +21,11 @@ import { ProgrammingExercise } from 'app/entities/programming/programming-exerci (onClick)="openRepoExportDialog($event)" /> `, + imports: [ButtonComponent], }) export class ProgrammingAssessmentRepoExportButtonComponent { + private modalService = inject(NgbModal); + readonly ButtonType = ButtonType; readonly ButtonSize = ButtonSize; readonly FeatureToggle = FeatureToggle; @@ -36,8 +40,6 @@ export class ProgrammingAssessmentRepoExportButtonComponent { // Icons faDownload = faDownload; - constructor(private modalService: NgbModal) {} - /** * Stops the propagation of the mouse event and updates the component instance * of the modalRef with this instance's values diff --git a/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component.ts b/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component.ts index e61c909e6893..41451632ef5e 100644 --- a/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component.ts +++ b/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; import { ProgrammingAssessmentRepoExportService, RepositoryExportOptions } from 'app/exercises/programming/assess/repo-export/programming-assessment-repo-export.service'; @@ -8,13 +8,25 @@ import { ProgrammingExercise } from 'app/entities/programming/programming-exerci import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { downloadZipFileFromResponse } from 'app/shared/util/download.util'; import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; +import { FeatureToggleDirective } from 'app/shared/feature-toggle/feature-toggle.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-exercise-scores-repo-export-dialog', templateUrl: './programming-assessment-repo-export-dialog.component.html', styles: ['textarea { width: 100%; }'], + imports: [FormsModule, TranslateDirective, HelpIconComponent, FormDateTimePickerComponent, FeatureToggleDirective, FaIconComponent], }) export class ProgrammingAssessmentRepoExportDialogComponent implements OnInit { + private exerciseService = inject(ExerciseService); + private repoExportService = inject(ProgrammingAssessmentRepoExportService); + activeModal = inject(NgbActiveModal); + private alertService = inject(AlertService); + @Input() programmingExercises: ProgrammingExercise[]; // Either a participationId list or a participantIdentifier (student login or team short name) list can be provided that is used for exporting the repos. // Priority: participationId >> participantIdentifier. @@ -31,13 +43,6 @@ export class ProgrammingAssessmentRepoExportDialogComponent implements OnInit { // Icons faCircleNotch = faCircleNotch; - constructor( - private exerciseService: ExerciseService, - private repoExportService: ProgrammingAssessmentRepoExportService, - public activeModal: NgbActiveModal, - private alertService: AlertService, - ) {} - ngOnInit() { this.isLoading = true; this.exportInProgress = false; diff --git a/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export.service.ts b/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export.service.ts index 03a0e49a2bdb..83045e49cf77 100644 --- a/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export.service.ts +++ b/src/main/webapp/app/exercises/programming/assess/repo-export/programming-assessment-repo-export.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import dayjs from 'dayjs/esm'; @@ -16,11 +16,11 @@ export type RepositoryExportOptions = { @Injectable({ providedIn: 'root' }) export class ProgrammingAssessmentRepoExportService { + private http = inject(HttpClient); + // TODO: We should move this endpoint to api/programming-exercises. public resourceUrl = 'api/programming-exercises'; - constructor(private http: HttpClient) {} - /** * Exports repositories to the server by their participant identifiers * @param {number} exerciseId - Id of the exercise diff --git a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component.ts index 74b0f35dfab0..4ff117dc795b 100644 --- a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component.ts +++ b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel-title.component.ts @@ -12,7 +12,6 @@ enum FileStatus { selector: 'jhi-git-diff-file-panel-title', templateUrl: './git-diff-file-panel-title.component.html', styleUrls: ['./git-diff-file-panel-title.component.scss'], - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslateDirective, CommonModule], }) diff --git a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.ts index 0417e9239618..8f3ae5c388f7 100644 --- a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.ts +++ b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file-panel.component.ts @@ -5,15 +5,15 @@ import { GitDiffFilePanelTitleComponent } from 'app/exercises/programming/git-di import { GitDiffLineStatComponent } from 'app/exercises/programming/git-diff-report/git-diff-line-stat.component'; import { GitDiffFileComponent } from 'app/exercises/programming/git-diff-report/git-diff-file.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; +import { NgbAccordionModule, NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'jhi-git-diff-file-panel', templateUrl: './git-diff-file-panel.component.html', styleUrls: ['./git-diff-file-panel.component.scss'], encapsulation: ViewEncapsulation.None, - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [GitDiffFilePanelTitleComponent, GitDiffLineStatComponent, GitDiffFileComponent, ArtemisSharedModule], + imports: [GitDiffFilePanelTitleComponent, GitDiffLineStatComponent, GitDiffFileComponent, ArtemisSharedModule, NgbAccordionModule, NgbCollapse], }) export class GitDiffFilePanelComponent { protected readonly faAngleUp = faAngleUp; diff --git a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component.ts index 055875de5a6a..5125c6078782 100644 --- a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component.ts +++ b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-file.component.ts @@ -7,7 +7,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; selector: 'jhi-git-diff-file', templateUrl: './git-diff-file.component.html', encapsulation: ViewEncapsulation.None, - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MonacoDiffEditorComponent, TranslateDirective], }) diff --git a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component.ts index b82ab8d02c02..4711b1fef884 100644 --- a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component.ts +++ b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-line-stat.component.ts @@ -4,7 +4,6 @@ import { ChangeDetectionStrategy, Component, computed, input } from '@angular/co selector: 'jhi-git-diff-line-stat', templateUrl: './git-diff-line-stat.component.html', styleUrls: ['./git-diff-line-stat.component.scss'], - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, }) export class GitDiffLineStatComponent { diff --git a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component.ts index 96a16d6986ee..268a63c97a24 100644 --- a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component.ts +++ b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report-modal.component.ts @@ -11,7 +11,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-git-diff-report-modal', templateUrl: './git-diff-report-modal.component.html', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [GitDiffReportComponent, TranslateDirective], }) @@ -32,21 +31,18 @@ export class GitDiffReportModalComponent { readonly rightCommitFileContentByPath = signal | undefined>(undefined); constructor() { - effect( - () => { - // We call the signal here to ensure the effect always runs when the report changes. - this.report(); - const diffBetweenTemplateAndSolution = this.diffForTemplateAndSolution(); - untracked(async () => { - if (diffBetweenTemplateAndSolution) { - await this.loadFilesForTemplateAndSolution(); - } else { - await this.loadRepositoryFilesForParticipationsFromCacheIfAvailable(); - } - }); - }, - { allowSignalWrites: true }, - ); + effect(() => { + // We call the signal here to ensure the effect always runs when the report changes. + this.report(); + const diffBetweenTemplateAndSolution = this.diffForTemplateAndSolution(); + untracked(async () => { + if (diffBetweenTemplateAndSolution) { + await this.loadFilesForTemplateAndSolution(); + } else { + await this.loadRepositoryFilesForParticipationsFromCacheIfAvailable(); + } + }); + }); } private async loadFilesForTemplateAndSolution(): Promise { diff --git a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component.ts b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component.ts index 7edc4f32aaf3..c3e200845b9f 100644 --- a/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component.ts +++ b/src/main/webapp/app/exercises/programming/git-diff-report/git-diff-report.component.ts @@ -19,7 +19,6 @@ interface DiffInformation { @Component({ selector: 'jhi-git-diff-report', templateUrl: './git-diff-report.component.html', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [GitDiffLineStatComponent, ArtemisSharedModule, ArtemisSharedComponentModule, GitDiffFilePanelComponent], }) diff --git a/src/main/webapp/app/exercises/programming/manage/build-plan-editor.component.ts b/src/main/webapp/app/exercises/programming/manage/build-plan-editor.component.ts index b160bf88b2be..a85a4ad265b9 100644 --- a/src/main/webapp/app/exercises/programming/manage/build-plan-editor.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/build-plan-editor.component.ts @@ -1,5 +1,6 @@ -import { AfterViewInit, Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { AfterViewInit, Component, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { faCircleNotch, faPlayCircle } from '@fortawesome/free-solid-svg-icons'; +import { UpdatingResultComponent } from 'app/exercises/shared/result/updating-result.component'; import { onError } from 'app/shared/util/global.utils'; import { AlertService } from 'app/core/util/alert.service'; import { BuildPlanService } from 'app/exercises/programming/manage/services/build-plan.service'; @@ -8,14 +9,25 @@ import { ActivatedRoute } from '@angular/router'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { CodeEditorHeaderComponent } from '../shared/code-editor/header/code-editor-header.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-build-plan-editor', templateUrl: './build-plan-editor.component.html', styleUrls: ['./build-plan-editor.scss'], encapsulation: ViewEncapsulation.None, + imports: [TranslateDirective, UpdatingResultComponent, NgbTooltip, FaIconComponent, CodeEditorHeaderComponent, MonacoEditorComponent, ArtemisTranslatePipe], }) export class BuildPlanEditorComponent implements AfterViewInit, OnInit { + private buildPlanService = inject(BuildPlanService); + private programmingExerciseService = inject(ProgrammingExerciseService); + private alertService = inject(AlertService); + private activatedRoute = inject(ActivatedRoute); + // Icons readonly faCircleNotch = faCircleNotch; readonly farPlayCircle = faPlayCircle; @@ -29,13 +41,6 @@ export class BuildPlanEditorComponent implements AfterViewInit, OnInit { programmingExercise: ProgrammingExercise; buildPlan: BuildPlan | undefined; - constructor( - private buildPlanService: BuildPlanService, - private programmingExerciseService: ProgrammingExerciseService, - private alertService: AlertService, - private activatedRoute: ActivatedRoute, - ) {} - /** * @function ngOnInit * Sets the exercise and corresponding build plan based on the exerciseId in the current URL. diff --git a/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-and-editor-container.component.ts b/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-and-editor-container.component.ts index f7fd38dc5515..3f73de34af1f 100644 --- a/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-and-editor-container.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-and-editor-container.component.ts @@ -1,23 +1,37 @@ import { Component, ViewChild } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { Location } from '@angular/common'; -import { AlertService } from 'app/core/util/alert.service'; +import { ProgrammingExerciseStudentTriggerBuildButtonComponent } from 'app/exercises/programming/shared/actions/programming-exercise-student-trigger-build-button.component'; +import { CodeEditorContainerComponent } from 'app/exercises/programming/shared/code-editor/container/code-editor-container.component'; +import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-headers/included-in-score-badge.component'; import { UpdatingResultComponent } from 'app/exercises/shared/result/updating-result.component'; import { CodeEditorInstructorBaseContainerComponent } from 'app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component'; -import { DomainService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain.service'; -import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; -import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; import { ProgrammingExerciseEditableInstructionComponent } from 'app/exercises/programming/manage/instructions-editor/programming-exercise-editable-instruction.component'; import { IncludedInOverallScore } from 'app/entities/exercise.model'; import { faCircleNotch, faPlus, faTimes, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; -import { CourseExerciseService } from 'app/exercises/shared/course-exercises/course-exercise.service'; import { IrisSettings } from 'app/entities/iris/settings/iris-settings.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ProgrammingExerciseInstructorExerciseStatusComponent } from '../status/programming-exercise-instructor-exercise-status.component'; +import { NgbDropdown, NgbDropdownButtonItem, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'jhi-code-editor-instructor', templateUrl: './code-editor-instructor-and-editor-container.component.html', styleUrl: 'code-editor-instructor-and-editor-container.scss', + imports: [ + FaIconComponent, + TranslateDirective, + CodeEditorContainerComponent, + IncludedInScoreBadgeComponent, + ProgrammingExerciseInstructorExerciseStatusComponent, + NgbDropdown, + NgbDropdownToggle, + NgbDropdownMenu, + NgbDropdownButtonItem, + NgbDropdownItem, + UpdatingResultComponent, + ProgrammingExerciseStudentTriggerBuildButtonComponent, + ProgrammingExerciseEditableInstructionComponent, + ], }) export class CodeEditorInstructorAndEditorContainerComponent extends CodeEditorInstructorBaseContainerComponent { @ViewChild(UpdatingResultComponent, { static: false }) resultComp: UpdatingResultComponent; @@ -32,18 +46,4 @@ export class CodeEditorInstructorAndEditorContainerComponent extends CodeEditorI faTimesCircle = faTimesCircle; irisSettings?: IrisSettings; - - constructor( - router: Router, - exerciseService: ProgrammingExerciseService, - courseExerciseService: CourseExerciseService, - domainService: DomainService, - programmingExerciseParticipationService: ProgrammingExerciseParticipationService, - location: Location, - participationService: ParticipationService, - route: ActivatedRoute, - alertService: AlertService, - ) { - super(router, exerciseService, courseExerciseService, domainService, programmingExerciseParticipationService, location, participationService, route, alertService); - } } diff --git a/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component.ts b/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component.ts index 0b6e1efd5d89..ccc3557d3f11 100644 --- a/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-instructor-base-container.component.ts @@ -1,4 +1,5 @@ -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; +import { CodeEditorContainerComponent } from 'app/exercises/programming/shared/code-editor/container/code-editor-container.component'; import { Observable, Subscription, of, throwError } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; import { Location } from '@angular/common'; @@ -9,14 +10,12 @@ import { Participation } from 'app/entities/participation/participation.model'; import { ButtonSize } from 'app/shared/components/button.component'; import { DomainService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain.service'; import { TemplateProgrammingExerciseParticipation } from 'app/entities/participation/template-programming-exercise-participation.model'; -import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { ExerciseType } from 'app/entities/exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; import { DomainChange, DomainType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; -import { CodeEditorContainerComponent } from '../../shared/code-editor/container/code-editor-container.component'; import { Course } from 'app/entities/course.model'; import { CourseExerciseService } from 'app/exercises/shared/course-exercises/course-exercise.service'; @@ -42,10 +41,21 @@ export enum LOADING_STATE { DELETING_ASSIGNMENT_REPO = 'DELETING_ASSIGNMENT_REPO', } -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class CodeEditorInstructorBaseContainerComponent implements OnInit, OnDestroy { @ViewChild(CodeEditorContainerComponent, { static: false }) codeEditorContainer: CodeEditorContainerComponent; + private router = inject(Router); + private exerciseService = inject(ProgrammingExerciseService); + private courseExerciseService = inject(CourseExerciseService); + private domainService = inject(DomainService); + private location = inject(Location); + private participationService = inject(ParticipationService); + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + ButtonSize = ButtonSize; REPOSITORY = REPOSITORY; LOADING_STATE = LOADING_STATE; @@ -75,18 +85,6 @@ export abstract class CodeEditorInstructorBaseContainerComponent implements OnIn // State variables loadingState = LOADING_STATE.CLEAR; - protected constructor( - protected router: Router, - private exerciseService: ProgrammingExerciseService, - private courseExerciseService: CourseExerciseService, - private domainService: DomainService, - private programmingExerciseParticipationService: ProgrammingExerciseParticipationService, - private location: Location, - private participationService: ParticipationService, - protected route: ActivatedRoute, - private alertService: AlertService, - ) {} - /** * Initialize the route params subscription. * On route param change load the exercise and the selected participation OR the test repository. diff --git a/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-management-routing.module.ts b/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-management-routing.module.ts index f2b7ac2605a5..a009e0ff1a75 100644 --- a/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-management-routing.module.ts +++ b/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-management-routing.module.ts @@ -1,14 +1,17 @@ import { RouterModule, Routes } from '@angular/router'; -import { CodeEditorInstructorAndEditorContainerComponent } from 'app/exercises/programming/manage/code-editor/code-editor-instructor-and-editor-container.component'; + import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { CodeEditorInstructorAndEditorOrionContainerComponent } from 'app/orion/management/code-editor-instructor-and-editor-orion-container.component'; + import { NgModule } from '@angular/core'; import { Authority } from 'app/shared/constants/authority.constants'; const routes: Routes = [ { path: 'test', - component: CodeEditorInstructorAndEditorContainerComponent, + loadComponent: () => + import('app/exercises/programming/manage/code-editor/code-editor-instructor-and-editor-container.component').then( + (m) => m.CodeEditorInstructorAndEditorContainerComponent, + ), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.editor.home.title', @@ -17,7 +20,8 @@ const routes: Routes = [ }, { path: 'ide/test', - component: CodeEditorInstructorAndEditorOrionContainerComponent, + loadComponent: () => + import('app/orion/management/code-editor-instructor-and-editor-orion-container.component').then((m) => m.CodeEditorInstructorAndEditorOrionContainerComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.editor.home.title', @@ -26,7 +30,8 @@ const routes: Routes = [ }, { path: 'ide/:participationId', - component: CodeEditorInstructorAndEditorOrionContainerComponent, + loadComponent: () => + import('app/orion/management/code-editor-instructor-and-editor-orion-container.component').then((m) => m.CodeEditorInstructorAndEditorOrionContainerComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.editor.home.title', @@ -35,7 +40,10 @@ const routes: Routes = [ }, { path: ':participationId', - component: CodeEditorInstructorAndEditorContainerComponent, + loadComponent: () => + import('app/exercises/programming/manage/code-editor/code-editor-instructor-and-editor-container.component').then( + (m) => m.CodeEditorInstructorAndEditorContainerComponent, + ), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.editor.home.title', @@ -44,7 +52,10 @@ const routes: Routes = [ }, { path: 'auxiliary/:repositoryId', - component: CodeEditorInstructorAndEditorContainerComponent, + loadComponent: () => + import('app/exercises/programming/manage/code-editor/code-editor-instructor-and-editor-container.component').then( + (m) => m.CodeEditorInstructorAndEditorContainerComponent, + ), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.editor.home.title', diff --git a/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-management.module.ts b/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-management.module.ts index 4eb51f50768f..d00b4c8d9978 100644 --- a/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-management.module.ts +++ b/src/main/webapp/app/exercises/programming/manage/code-editor/code-editor-management.module.ts @@ -28,7 +28,8 @@ import { IrisModule } from 'app/iris/iris.module'; SubmissionResultStatusModule, ArtemisSharedComponentModule, IrisModule, + CodeEditorInstructorAndEditorContainerComponent, + CodeEditorInstructorAndEditorOrionContainerComponent, ], - declarations: [CodeEditorInstructorAndEditorContainerComponent, CodeEditorInstructorAndEditorOrionContainerComponent], }) export class ArtemisCodeEditorManagementModule {} diff --git a/src/main/webapp/app/exercises/programming/manage/grading/charts/category-issues-chart.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/charts/category-issues-chart.component.ts index 236a85bf0cce..124a9a93658b 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/charts/category-issues-chart.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/charts/category-issues-chart.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnChanges } from '@angular/core'; import { IssuesMap } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/programming/static-code-analysis-category.model'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; export class IssueColumn { w: string; @@ -22,6 +23,7 @@ export class IssueColumn { `, + imports: [NgbTooltip], }) export class CategoryIssuesChartComponent implements OnChanges { @Input() issuesMap?: IssuesMap; @@ -32,7 +34,7 @@ export class CategoryIssuesChartComponent implements OnChanges { columns: IssueColumn[] = []; - ngOnChanges(): void { + ngOnChanges() { // set a minimum of 10 columns const numColumns = Math.max(this.maxNumberOfIssues, 10) + 1; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/charts/sca-category-distribution-chart.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/charts/sca-category-distribution-chart.component.ts index 58e2d7aa9dd3..4e08cc5727e9 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/charts/sca-category-distribution-chart.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/charts/sca-category-distribution-chart.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, inject } from '@angular/core'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { StaticCodeAnalysisCategory, StaticCodeAnalysisCategoryState } from 'app/entities/programming/static-code-analysis-category.model'; import { CategoryIssuesMap } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; @@ -7,6 +7,9 @@ import { getColor } from 'app/exercises/programming/manage/grading/charts/progra import { ProgrammingGradingChartsDirective } from 'app/exercises/programming/manage/grading/charts/programming-grading-charts.directive'; import { NgxChartsMultiSeriesDataEntry } from 'app/shared/chart/ngx-charts-datatypes'; import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { BarChartModule } from '@swimlane/ngx-charts'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; enum ScaChartBarTitle { PENALTY = 'Penalty', @@ -83,8 +86,12 @@ enum ScaChartBarTitle { `, + imports: [TranslateDirective, BarChartModule, ArtemisTranslatePipe], }) export class ScaCategoryDistributionChartComponent extends ProgrammingGradingChartsDirective implements OnChanges { + private translateService = inject(TranslateService); + private navigationUtilsService = inject(ArtemisNavigationUtilService); + @Input() categories: StaticCodeAnalysisCategory[]; @Input() categoryIssuesMap?: CategoryIssuesMap; @Input() exercise: ProgrammingExercise; @@ -97,17 +104,16 @@ export class ScaCategoryDistributionChartComponent extends ProgrammingGradingCha // ngx ngxData: NgxChartsMultiSeriesDataEntry[] = []; - constructor( - private translateService: TranslateService, - private navigationUtilsService: ArtemisNavigationUtilService, - ) { + constructor() { super(); + const translateService = this.translateService; + translateService.onLangChange.subscribe(() => { this.updateTranslations(); }); } - ngOnChanges(): void { + ngOnChanges() { this.ngxData = []; this.ngxColors.domain = []; // update colors for category table diff --git a/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-distribution-chart.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-distribution-chart.component.ts index 568402289fca..4bf9f20c9e82 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-distribution-chart.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-distribution-chart.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, inject } from '@angular/core'; import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { TestCaseStatsMap } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; @@ -8,6 +8,9 @@ import { ProgrammingGradingChartsDirective } from 'app/exercises/programming/man import { getTotalMaxPoints } from 'app/exercises/shared/exercise/exercise.utils'; import { NgxChartsMultiSeriesDataEntry } from 'app/shared/chart/ngx-charts-datatypes'; import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { BarChartModule } from '@swimlane/ngx-charts'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; enum TestCaseBarTitle { WEIGHT_EN = 'Weight', @@ -24,8 +27,12 @@ type TestCaseColors = { selector: 'jhi-test-case-distribution-chart', styleUrls: ['./sca-category-distribution-chart.scss'], templateUrl: './test-case-distribution-chart.component.html', + imports: [TranslateDirective, BarChartModule, ArtemisTranslatePipe], }) export class TestCaseDistributionChartComponent extends ProgrammingGradingChartsDirective implements OnInit, OnChanges { + private translateService = inject(TranslateService); + private navigationUtilService = inject(ArtemisNavigationUtilService); + @Input() testCases: ProgrammingExerciseTestCase[]; @Input() testCaseStatsMap?: TestCaseStatsMap; @Input() totalParticipations?: number; @@ -47,10 +54,7 @@ export class TestCaseDistributionChartComponent extends ProgrammingGradingCharts // array containing the ngx-dedicated objects in order to display the points chart ngxPointsData: NgxChartsMultiSeriesDataEntry[] = [{ name: '', series: [] as any[] }]; - constructor( - private translateService: TranslateService, - private navigationUtilService: ArtemisNavigationUtilService, - ) { + constructor() { super(); this.translateService.onLangChange.subscribe(() => { @@ -62,7 +66,7 @@ export class TestCaseDistributionChartComponent extends ProgrammingGradingCharts this.updateTranslation(); } - ngOnChanges(): void { + ngOnChanges() { if (this.testCases == undefined) { this.testCases = []; } diff --git a/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-passed-builds-chart.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-passed-builds-chart.component.ts index 03e4a076bc58..d1d4874de33e 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-passed-builds-chart.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/charts/test-case-passed-builds-chart.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnChanges } from '@angular/core'; import { TestCaseStats } from 'app/entities/programming/programming-exercise-test-case-statistics.model'; import { round } from 'app/shared/util/utils'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'jhi-test-case-passed-builds-chart', @@ -15,6 +16,7 @@ import { round } from 'app/shared/util/utils'; '.passed-bar { position: absolute; top: 0; left: 0; height: 10px; background-color: #28a745 }', '.failed-bar { position: absolute; top: 0; height: 10px; background-color: #dc3545 }', ], + imports: [NgbTooltip], }) export class TestCasePassedBuildsChartComponent implements OnChanges { @Input() testCaseStats?: TestCaseStats; @@ -24,7 +26,7 @@ export class TestCasePassedBuildsChartComponent implements OnChanges { failedPercent = 0; tooltip = ''; - ngOnChanges(): void { + ngOnChanges() { const totalPassedAndFailed = (this.testCaseStats?.numPassed ?? 0) + (this.testCaseStats?.numFailed ?? 0); if (totalPassedAndFailed > this.totalParticipations) { this.totalParticipations = totalPassedAndFailed; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/confirm-feedback-channel-creation-modal.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/confirm-feedback-channel-creation-modal.component.ts index 4a31192b6299..4beca713648e 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/confirm-feedback-channel-creation-modal.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/confirm-feedback-channel-creation-modal.component.ts @@ -6,7 +6,6 @@ import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; selector: 'jhi-confirm-feedback-channel-creation-modal', templateUrl: './confirm-feedback-channel-creation-modal.component.html', imports: [ArtemisSharedCommonModule], - standalone: true, }) export class ConfirmFeedbackChannelCreationModalComponent { protected readonly TRANSLATION_BASE = 'artemisApp.programmingExercise.configureGrading.feedbackAnalysis.feedbackDetailChannel.confirmationModal'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-affected-students-modal.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-affected-students-modal.component.ts index ed5b2d2fb36a..4cc518982ea2 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-affected-students-modal.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-affected-students-modal.component.ts @@ -11,7 +11,6 @@ import { faSpinner } from '@fortawesome/free-solid-svg-icons'; templateUrl: './feedback-affected-students-modal.component.html', imports: [ArtemisSharedCommonModule, ArtemisSharedComponentModule], providers: [FeedbackAnalysisService], - standalone: true, }) export class AffectedStudentsModalComponent { courseId = input.required(); diff --git a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-detail-channel-modal.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-detail-channel-modal.component.ts index 5e9577ea889d..5d40561709ee 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-detail-channel-modal.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-detail-channel-modal.component.ts @@ -1,5 +1,5 @@ import { Component, inject, input, output, signal } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ChannelDTO } from 'app/entities/metis/conversation/channel.model'; import { FeedbackDetail } from 'app/exercises/programming/manage/grading/feedback-analysis/feedback-analysis.service'; @@ -10,8 +10,7 @@ import { AlertService } from 'app/core/util/alert.service'; @Component({ selector: 'jhi-feedback-detail-channel-modal', templateUrl: './feedback-detail-channel-modal.component.html', - imports: [ArtemisSharedCommonModule], - standalone: true, + imports: [ArtemisSharedCommonModule, FormsModule, ReactiveFormsModule], }) export class FeedbackDetailChannelModalComponent { protected readonly TRANSLATION_BASE = 'artemisApp.programmingExercise.configureGrading.feedbackAnalysis.feedbackDetailChannel'; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-filter-modal.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-filter-modal.component.ts index cf7632c82337..c5a20a3941a6 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-filter-modal.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-filter-modal.component.ts @@ -17,7 +17,6 @@ export interface FilterData { templateUrl: './feedback-filter-modal.component.html', imports: [RangeSliderComponent, ArtemisSharedCommonModule], providers: [FeedbackAnalysisService], - standalone: true, }) export class FeedbackFilterModalComponent { private localStorage = inject(LocalStorageService); diff --git a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-modal.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-modal.component.ts index af60c0af8569..abbead9b4518 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-modal.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-modal.component.ts @@ -7,7 +7,6 @@ import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; selector: 'jhi-feedback-modal', templateUrl: './feedback-modal.component.html', imports: [ArtemisSharedCommonModule], - standalone: true, }) export class FeedbackModalComponent { feedbackDetail = input.required(); diff --git a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/feedback-analysis.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/feedback-analysis.component.ts index a787eeecf13a..c740ebbcd9ab 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/feedback-analysis.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/feedback-analysis/feedback-analysis.component.ts @@ -1,9 +1,8 @@ import { Component, computed, effect, inject, input, signal, untracked } from '@angular/core'; import { FeedbackAnalysisService, FeedbackChannelRequestDTO, FeedbackDetail } from './feedback-analysis.service'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbModule, NgbPagination } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; -import { faCircleQuestion, faFilter, faMessage, faSort, faSortDown, faSortUp, faSpinner, faUsers } from '@fortawesome/free-solid-svg-icons'; -import { facDetails } from '../../../../../../content/icons/icons'; +import { faCircleQuestion, faFilter, faMessage, faSort, faSpinner, faUsers } from '@fortawesome/free-solid-svg-icons'; import { SearchResult, SortingOrder } from 'app/shared/table/pageable-table'; import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; import { FeedbackModalComponent } from 'app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-modal.component'; @@ -15,13 +14,13 @@ import { AffectedStudentsModalComponent } from 'app/exercises/programming/manage import { FeedbackDetailChannelModalComponent } from 'app/exercises/programming/manage/grading/feedback-analysis/Modal/feedback-detail-channel-modal.component'; import { ChannelDTO } from 'app/entities/metis/conversation/channel.model'; import { Router } from '@angular/router'; +import { facDetails } from 'app/icons/icons'; @Component({ selector: 'jhi-feedback-analysis', templateUrl: './feedback-analysis.component.html', styleUrls: ['./feedback-analysis.component.scss'], - standalone: true, - imports: [ArtemisSharedCommonModule, SortIconComponent], + imports: [ArtemisSharedCommonModule, SortIconComponent, NgbModule, NgbPagination], providers: [FeedbackAnalysisService], }) export class FeedbackAnalysisComponent { @@ -48,8 +47,6 @@ export class FeedbackAnalysisComponent { readonly TRANSLATION_BASE = 'artemisApp.programmingExercise.configureGrading.feedbackAnalysis'; readonly faSort = faSort; - readonly faSortUp = faSortUp; - readonly faSortDown = faSortDown; readonly faFilter = faFilter; readonly facDetails = facDetails; readonly faUsers = faUsers; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-actions.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-actions.component.ts index 6c91acef5676..7f38b3748fab 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-actions.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-actions.component.ts @@ -1,5 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; +import { ProgrammingExerciseReEvaluateButtonComponent } from 'app/exercises/programming/shared/actions/programming-exercise-re-evaluate-button.component'; +import { ProgrammingExerciseTriggerAllButtonComponent } from 'app/exercises/programming/shared/actions/programming-exercise-trigger-all-button.component'; /** * The actions of the grading page: @@ -12,6 +14,7 @@ import { ProgrammingExercise } from 'app/entities/programming/programming-exerci `, + imports: [ProgrammingExerciseReEvaluateButtonComponent, ProgrammingExerciseTriggerAllButtonComponent], }) export class ProgrammingExerciseConfigureGradingActionsComponent { @Input() exercise: ProgrammingExercise; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-status.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-status.component.ts index cf73c3a9e075..8d3d5b687b86 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-status.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading-status.component.ts @@ -1,5 +1,9 @@ import { Component, Input } from '@angular/core'; import { faCheckCircle, faExclamationTriangle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; /** * Two status indicators for the test case table: @@ -79,6 +83,7 @@ import { faCheckCircle, faExclamationTriangle, faQuestionCircle } from '@fortawe `, + imports: [FaIconComponent, TranslateDirective, NgbTooltip, ArtemisTranslatePipe], }) export class ProgrammingExerciseConfigureGradingStatusComponent { @Input() exerciseIsReleasedAndHasResults: boolean; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading.component.ts index 13ce8237d382..f864a080c423 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-configure-grading.component.ts @@ -1,5 +1,5 @@ -import { Location } from '@angular/common'; -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Location, NgClass, NgTemplateOutlet } from '@angular/common'; +import { Component, OnDestroy, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { faQuestionCircle, faSort, faSortDown, faSortUp, faSquare } from '@fortawesome/free-solid-svg-icons'; import { TranslateService } from '@ngx-translate/core'; @@ -17,12 +17,29 @@ import { ProgrammingExerciseGradingService, StaticCodeAnalysisCategoryUpdate } f import { ProgrammingExerciseWebsocketService } from 'app/exercises/programming/manage/services/programming-exercise-websocket.service'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { SubmissionPolicyService } from 'app/exercises/programming/manage/services/submission-policy.service'; +import { SubmissionPolicyUpdateComponent } from 'app/exercises/shared/submission-policy/submission-policy-update.component'; import { ComponentCanDeactivate } from 'app/shared/guard/can-deactivate.model'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; import { differenceBy as _differenceBy, differenceWith as _differenceWith, intersectionWith as _intersectionWith, unionBy as _unionBy } from 'lodash-es'; import { Observable, Subscription, of, zip } from 'rxjs'; import { catchError, distinctUntilChanged, map, take, tap } from 'rxjs/operators'; import { ProgrammingExerciseTaskService } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ProgrammingExerciseConfigureGradingStatusComponent } from './programming-exercise-configure-grading-status.component'; +import { ProgrammingExerciseConfigureGradingActionsComponent } from './programming-exercise-configure-grading-actions.component'; +import { ProgrammingExerciseGradingSubmissionPolicyConfigurationActionsComponent } from './programming-exercise-grading-submission-policy-configuration-actions.component'; +import { NgbAlert, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ProgrammingExerciseGradingTasksTableComponent } from './tasks/programming-exercise-grading-tasks-table.component'; +import { TestCaseDistributionChartComponent } from './charts/test-case-distribution-chart.component'; +import { ProgrammingExerciseGradingTableActionsComponent } from './programming-exercise-grading-table-actions.component'; +import { NgxDatatableModule } from '@siemens/ngx-datatable'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormsModule } from '@angular/forms'; +import { TableEditableFieldComponent } from 'app/shared/table/table-editable-field.component'; +import { CategoryIssuesChartComponent } from './charts/category-issues-chart.component'; +import { ScaCategoryDistributionChartComponent } from './charts/sca-category-distribution-chart.component'; +import { FeedbackAnalysisComponent } from './feedback-analysis/feedback-analysis.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; /** * Describes the editableField @@ -65,8 +82,43 @@ export type Table = 'testCases' | 'codeAnalysis'; styleUrls: ['./programming-exercise-configure-grading.scss'], encapsulation: ViewEncapsulation.None, providers: [ProgrammingExerciseTaskService], + imports: [ + NgClass, + TranslateDirective, + NgTemplateOutlet, + ProgrammingExerciseConfigureGradingStatusComponent, + ProgrammingExerciseConfigureGradingActionsComponent, + ProgrammingExerciseGradingSubmissionPolicyConfigurationActionsComponent, + SubmissionPolicyUpdateComponent, + NgbAlert, + ProgrammingExerciseGradingTasksTableComponent, + TestCaseDistributionChartComponent, + ProgrammingExerciseGradingTableActionsComponent, + NgxDatatableModule, + FaIconComponent, + NgbTooltip, + FormsModule, + TableEditableFieldComponent, + CategoryIssuesChartComponent, + ScaCategoryDistributionChartComponent, + FeedbackAnalysisComponent, + ArtemisTranslatePipe, + ], }) export class ProgrammingExerciseConfigureGradingComponent implements OnInit, OnDestroy, ComponentCanDeactivate { + private accountService = inject(AccountService); + private gradingService = inject(ProgrammingExerciseGradingService); + private programmingExerciseService = inject(ProgrammingExerciseService); + private programmingExerciseSubmissionPolicyService = inject(SubmissionPolicyService); + private programmingExerciseWebsocketService = inject(ProgrammingExerciseWebsocketService); + private programmingExerciseTaskService = inject(ProgrammingExerciseTaskService); + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + private translateService = inject(TranslateService); + private location = inject(Location); + private router = inject(Router); + private courseManagementService = inject(CourseManagementService); + readonly EditableField = EditableField; readonly CategoryState = StaticCodeAnalysisCategoryState; readonly Visibility = Visibility; @@ -161,21 +213,6 @@ export class ProgrammingExerciseConfigureGradingComponent implements OnInit, OnD this.updateTestCaseFilter(); } - constructor( - private accountService: AccountService, - private gradingService: ProgrammingExerciseGradingService, - private programmingExerciseService: ProgrammingExerciseService, - private programmingExerciseSubmissionPolicyService: SubmissionPolicyService, - private programmingExerciseWebsocketService: ProgrammingExerciseWebsocketService, - private programmingExerciseTaskService: ProgrammingExerciseTaskService, - private route: ActivatedRoute, - private alertService: AlertService, - private translateService: TranslateService, - private location: Location, - private router: Router, - private courseManagementService: CourseManagementService, - ) {} - /** * Subscribes to the route params to get the current exerciseId. * Uses the exerciseId to subscribe to the newest value of the exercise's test cases. diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-dirty-warning.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-dirty-warning.component.ts index 197d3501fae6..a20231c7616f 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-dirty-warning.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-dirty-warning.component.ts @@ -1,8 +1,11 @@ -import { Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; +import { Component, Input, OnChanges, OnDestroy, SimpleChanges, inject } from '@angular/core'; import { ProgrammingExerciseWebsocketService } from 'app/exercises/programming/manage/services/programming-exercise-websocket.service'; import { tap } from 'rxjs/operators'; import { Subscription } from 'rxjs'; import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; /** * Two status indicators for the test case table: @@ -21,8 +24,11 @@ import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; /> } `, + imports: [FaIconComponent, NgbTooltip, ArtemisTranslatePipe], }) export class ProgrammingExerciseGradingDirtyWarningComponent implements OnChanges, OnDestroy { + private programmingExerciseWebsocketService = inject(ProgrammingExerciseWebsocketService); + @Input() programmingExerciseId: number; @Input() hasUpdatedGradingConfigInitialValue: boolean; @@ -32,8 +38,6 @@ export class ProgrammingExerciseGradingDirtyWarningComponent implements OnChange // Icons faExclamationTriangle = faExclamationTriangle; - constructor(private programmingExerciseWebsocketService: ProgrammingExerciseWebsocketService) {} - /** * Set the initial updated test case value on the first change of the property. * Subscribe for the test case state. This component is only visible if the programming exercise has updated (= dirty) test cases. diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-submission-policy-configuration-actions.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-submission-policy-configuration-actions.component.ts index 7e5156faaa52..6b6ad0c342cf 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-submission-policy-configuration-actions.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-submission-policy-configuration-actions.component.ts @@ -3,6 +3,7 @@ import { faSave } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { SubmissionPolicyType } from 'app/entities/submission-policy.model'; import { ButtonType } from 'app/shared/components/button.component'; +import { ButtonComponent } from 'app/shared/components/button.component'; /** * The actions of the submission policy configuration: @@ -43,6 +44,7 @@ import { ButtonType } from 'app/shared/components/button.component'; } `, + imports: [ButtonComponent], }) export class ProgrammingExerciseGradingSubmissionPolicyConfigurationActionsComponent { readonly ButtonType = ButtonType; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-table-actions.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-table-actions.component.ts index ae956e5b7a96..ee6496ae7ff4 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-table-actions.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading-table-actions.component.ts @@ -1,10 +1,12 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { faCopy } from '@fortawesome/free-solid-svg-icons'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ExerciseType } from 'app/entities/exercise.model'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { GradingTab } from 'app/exercises/programming/manage/grading/programming-exercise-configure-grading.component'; import { ExerciseImportWrapperComponent } from 'app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; /** * The actions of the test case table: @@ -14,8 +16,11 @@ import { ExerciseImportWrapperComponent } from 'app/exercises/shared/import/exer @Component({ selector: 'jhi-programming-exercise-grading-table-actions', templateUrl: './programming-exercise-grading-table-actions.component.html', + imports: [FaIconComponent, TranslateDirective], }) export class ProgrammingExerciseGradingTableActionsComponent { + private modalService = inject(NgbModal); + readonly faCopy = faCopy; @Input() exercise: ProgrammingExercise; @Input() hasUnsavedChanges: boolean; @@ -26,8 +31,6 @@ export class ProgrammingExerciseGradingTableActionsComponent { @Output() onReset = new EventEmitter(); @Output() onCategoryImport = new EventEmitter(); - constructor(private modalService: NgbModal) {} - openImportModal() { const modalRef = this.modalService.open(ExerciseImportWrapperComponent, { size: 'lg', backdrop: 'static' }); modalRef.componentInstance.exerciseType = ExerciseType.PROGRAMMING; diff --git a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading.module.ts b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading.module.ts index 705638a44789..d8dd1a51f35a 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading.module.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/programming-exercise-grading.module.ts @@ -35,8 +35,6 @@ import { FeedbackAnalysisComponent } from 'app/exercises/programming/manage/grad SubmissionPolicyUpdateModule, BarChartModule, FeedbackAnalysisComponent, - ], - declarations: [ ProgrammingExerciseConfigureGradingComponent, ProgrammingExerciseConfigureGradingStatusComponent, ProgrammingExerciseConfigureGradingActionsComponent, diff --git a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts index ec88a4dc9ae1..c5f037d7151a 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-grading-tasks-table.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { ProgrammingExerciseTaskService } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task.service'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { Course } from 'app/entities/course.model'; @@ -9,6 +9,13 @@ import { Observable, Subject } from 'rxjs'; import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { isExamExercise } from 'app/shared/util/utils'; import { ProgrammingExerciseServerSideTask } from 'app/entities/programming-exercise-task.model'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ProgrammingExerciseTaskComponent } from './programming-exercise-task/programming-exercise-task.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; type Sort = { by: 'name' | 'weight' | 'multiplier' | 'bonusPoints' | 'visibility' | 'resulting' | 'type'; @@ -21,8 +28,11 @@ type TaskComparator = (a: ProgrammingExerciseTask | ProgrammingExerciseTestCase, selector: 'jhi-programming-exercise-grading-tasks-table', templateUrl: './programming-exercise-grading-tasks-table.component.html', styleUrls: ['./programming-exercise-grading-tasks-table.scss'], + imports: [ButtonComponent, TranslateDirective, FormsModule, NgbTooltip, FaIconComponent, ProgrammingExerciseTaskComponent, ArtemisTranslatePipe], }) export class ProgrammingExerciseGradingTasksTableComponent implements OnInit { + private taskService = inject(ProgrammingExerciseTaskService); + @Input() exercise: ProgrammingExercise; @Input() course: Course; @Input() gradingStatisticsObservable: Observable; @@ -41,14 +51,12 @@ export class ProgrammingExerciseGradingTasksTableComponent implements OnInit { currentSort: Sort | undefined; - isExamExercise: boolean = false; + isExamExercise = false; get ignoreInactive() { return this.taskService.ignoreInactive; } - constructor(private taskService: ProgrammingExerciseTaskService) {} - ngOnInit(): void { this.allTasksExpandedSubject = new Subject(); this.gradingStatisticsObservable.subscribe((gradingStatistics) => { diff --git a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts index 3021e5e548f2..9c9fd15cbd65 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task.service.ts @@ -1,5 +1,5 @@ import { HttpClient, HttpErrorResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ProgrammingExerciseServerSideTask } from 'app/entities/programming-exercise-task.model'; import { Observable, catchError, of, tap } from 'rxjs'; import { Exercise } from 'app/entities/exercise.model'; @@ -15,6 +15,10 @@ import { map, mergeMap } from 'rxjs/operators'; @Injectable() export class ProgrammingExerciseTaskService { + private http = inject(HttpClient); + private alertService = inject(AlertService); + private gradingService = inject(ProgrammingExerciseGradingService); + exercise: ProgrammingExercise; course: Course; gradingStatistics: ProgrammingExerciseGradingStatistics; @@ -28,12 +32,6 @@ export class ProgrammingExerciseTaskService { public resourceUrl = 'api/programming-exercises'; - constructor( - private http: HttpClient, - private alertService: AlertService, - private gradingService: ProgrammingExerciseGradingService, - ) {} - get totalWeights() { return sum(this.testCases.map(({ weight }) => weight ?? 0)); } diff --git a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task/programming-exercise-task.component.ts b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task/programming-exercise-task.component.ts index 837c0210f76d..9ad4da1ce5b5 100644 --- a/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task/programming-exercise-task.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/grading/tasks/programming-exercise-task/programming-exercise-task.component.ts @@ -1,16 +1,23 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { faAngleDown, faAngleRight } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExerciseTask } from 'app/exercises/programming/manage/grading/tasks/programming-exercise-task'; import { ProgrammingExerciseTestCase, Visibility } from 'app/entities/programming/programming-exercise-test-case.model'; import { ProgrammingExerciseTaskService } from '../programming-exercise-task.service'; import { Subject } from 'rxjs'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormsModule } from '@angular/forms'; +import { TestCasePassedBuildsChartComponent } from '../../charts/test-case-passed-builds-chart.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-programming-exercise-task', templateUrl: './programming-exercise-task.component.html', styleUrls: ['../programming-exercise-grading-tasks-table.scss'], + imports: [FaIconComponent, FormsModule, TestCasePassedBuildsChartComponent, ArtemisTranslatePipe], }) export class ProgrammingExerciseTaskComponent implements OnInit { + private programmingExerciseTaskService = inject(ProgrammingExerciseTaskService); + @Input() index: number; @Input() task: ProgrammingExerciseTask; @Input() openSubject: Subject; @@ -31,8 +38,6 @@ export class ProgrammingExerciseTaskComponent implements OnInit { return this.programmingExerciseTaskService?.gradingStatistics?.numParticipations ?? 0; } - constructor(private programmingExerciseTaskService: ProgrammingExerciseTaskService) {} - ngOnInit(): void { this.openSubject.subscribe((open) => (this.open = open)); diff --git a/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.component.ts b/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.component.ts index 026a37bbdc0d..0a3581a8062f 100644 --- a/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.component.ts @@ -1,15 +1,23 @@ -import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, inject } from '@angular/core'; import { Subject, Subscription } from 'rxjs'; import { debounceTime, map, tap } from 'rxjs/operators'; import { ProgrammingExerciseInstructionAnalysisService } from 'app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.service'; import { ProblemStatementAnalysis } from 'app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.model'; import { faCheckCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TaskCountWarningComponent } from './task-count-warning/task-count-warning.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-programming-exercise-instruction-instructor-analysis', templateUrl: './programming-exercise-instruction-analysis.component.html', + imports: [FaIconComponent, NgbTooltip, TranslateDirective, TaskCountWarningComponent, ArtemisTranslatePipe], }) export class ProgrammingExerciseInstructionAnalysisComponent implements OnInit, OnChanges, OnDestroy { + private analysisService = inject(ProgrammingExerciseInstructionAnalysisService); + @Input() exerciseTestCases: string[]; @Input() problemStatement: string; @Input() taskRegex: RegExp; @@ -27,8 +35,6 @@ export class ProgrammingExerciseInstructionAnalysisComponent implements OnInit, faCheckCircle = faCheckCircle; faExclamationTriangle = faExclamationTriangle; - constructor(private analysisService: ProgrammingExerciseInstructionAnalysisService) {} - ngOnInit(): void { this.analysisSubscription = this.delayedAnalysisSubject .pipe( diff --git a/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.service.ts b/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.service.ts index 14f8dc19b04a..d65778147c7c 100644 --- a/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.service.ts +++ b/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/programming-exercise-instruction-analysis.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { uniq } from 'lodash-es'; import { RegExpLineNumberMatchArray, matchRegexWithLineNumbers } from 'app/shared/util/global.utils'; @@ -17,7 +17,7 @@ const REPEATED_TEST_CASE_TRANSLATION = 'artemisApp.programmingExercise.testCaseA */ @Injectable() export class ProgrammingExerciseInstructionAnalysisService { - constructor(private translateService: TranslateService) {} + private translateService = inject(TranslateService); /** * Given a programming exercise's problem statement, analyze the test cases contained (or not contained!) in it. diff --git a/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/task-count-warning/task-count-warning.component.ts b/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/task-count-warning/task-count-warning.component.ts index 1fd2ec9f876e..e6b6f52c532b 100644 --- a/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/task-count-warning/task-count-warning.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/instructions-editor/analysis/task-count-warning/task-count-warning.component.ts @@ -1,9 +1,13 @@ import { Component, Input } from '@angular/core'; import { faCheckCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-task-count-warning', templateUrl: './task-count-warning.component.html', + imports: [FaIconComponent, NgbTooltip, TranslateDirective], }) export class TaskCountWarningComponent { readonly faExclamationTriangle = faExclamationTriangle; diff --git a/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-editable-instruction.component.ts b/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-editable-instruction.component.ts index f945acc5a972..c755be38732a 100644 --- a/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-editable-instruction.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-editable-instruction.component.ts @@ -1,5 +1,6 @@ -import { AfterViewInit, Component, EventEmitter, HostListener, Input, OnChanges, OnDestroy, Output, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; +import { AfterViewInit, Component, EventEmitter, HostListener, Input, OnChanges, OnDestroy, Output, SimpleChanges, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { AlertService } from 'app/core/util/alert.service'; +import { ProgrammingExerciseInstructionComponent } from 'app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component'; import { Observable, Subject, Subscription, of, throwError } from 'rxjs'; import { catchError, map as rxMap, switchMap, tap } from 'rxjs/operators'; import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; @@ -18,14 +19,35 @@ import { FormulaAction } from 'app/shared/monaco-editor/model/actions/formula.ac import { TaskAction } from 'app/shared/monaco-editor/model/actions/task.action'; import { TestCaseAction } from 'app/shared/monaco-editor/model/actions/test-case.action'; import { TextEditorDomainAction } from 'app/shared/monaco-editor/model/actions/text-editor-domain-action.model'; +import { NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ProgrammingExerciseInstructionAnalysisComponent } from './analysis/programming-exercise-instruction-analysis.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-programming-exercise-editable-instructions', templateUrl: './programming-exercise-editable-instruction.component.html', styleUrls: ['./programming-exercise-editable-instruction.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + MarkdownEditorMonacoComponent, + ProgrammingExerciseInstructionComponent, + NgClass, + FaIconComponent, + TranslateDirective, + NgbTooltip, + ProgrammingExerciseInstructionAnalysisComponent, + ArtemisTranslatePipe, + ], }) export class ProgrammingExerciseEditableInstructionComponent implements AfterViewInit, OnChanges, OnDestroy { + private programmingExerciseService = inject(ProgrammingExerciseService); + private alertService = inject(AlertService); + private programmingExerciseParticipationService = inject(ProgrammingExerciseParticipationService); + private testCaseService = inject(ProgrammingExerciseGradingService); + participationValue: Participation; programmingExercise: ProgrammingExercise; @@ -95,13 +117,6 @@ export class ProgrammingExerciseEditableInstructionComponent implements AfterVie protected readonly MarkdownEditorHeight = MarkdownEditorHeight; - constructor( - private programmingExerciseService: ProgrammingExerciseService, - private alertService: AlertService, - private programmingExerciseParticipationService: ProgrammingExerciseParticipationService, - private testCaseService: ProgrammingExerciseGradingService, - ) {} - ngOnChanges(changes: SimpleChanges): void { if (hasExerciseChanged(changes)) { this.setupTestCaseSubscription(); diff --git a/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-instructions-editor.module.ts b/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-instructions-editor.module.ts index 5507f89b908c..61b8b32a15c2 100644 --- a/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-instructions-editor.module.ts +++ b/src/main/webapp/app/exercises/programming/manage/instructions-editor/programming-exercise-instructions-editor.module.ts @@ -9,8 +9,15 @@ import { ProgrammingExerciseInstructionAnalysisService } from 'app/exercises/pro import { TaskCountWarningComponent } from './analysis/task-count-warning/task-count-warning.component'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisProgrammingExerciseInstructionsRenderModule, ArtemisMarkdownEditorModule, ArtemisProgrammingExerciseStatusModule], - declarations: [ProgrammingExerciseEditableInstructionComponent, ProgrammingExerciseInstructionAnalysisComponent, TaskCountWarningComponent], + imports: [ + ArtemisSharedModule, + ArtemisProgrammingExerciseInstructionsRenderModule, + ArtemisMarkdownEditorModule, + ArtemisProgrammingExerciseStatusModule, + ProgrammingExerciseEditableInstructionComponent, + ProgrammingExerciseInstructionAnalysisComponent, + TaskCountWarningComponent, + ], providers: [ProgrammingExerciseInstructionAnalysisService], exports: [ArtemisProgrammingExerciseInstructionsRenderModule, ProgrammingExerciseEditableInstructionComponent], }) diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-create-buttons.component.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise-create-buttons.component.ts index 9c842a025b85..b07cbb6b9082 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-create-buttons.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-create-buttons.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { Course } from 'app/entities/course.model'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { faFileImport, faKeyboard, faPlus } from '@fortawesome/free-solid-svg-icons'; @@ -6,13 +6,21 @@ import { ExerciseImportWrapperComponent } from 'app/exercises/shared/import/exer import { ExerciseType } from 'app/entities/exercise.model'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { Router } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; +import { FeatureToggleLinkDirective } from 'app/shared/feature-toggle/feature-toggle-link.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FeatureToggleDirective } from 'app/shared/feature-toggle/feature-toggle.directive'; @Component({ selector: 'jhi-programming-exercise-create-buttons', templateUrl: './programming-exercise-create-buttons.component.html', + imports: [FeatureToggleLinkDirective, RouterLink, FaIconComponent, TranslateDirective, FeatureToggleDirective], }) export class ProgrammingExerciseCreateButtonsComponent { + private router = inject(Router); + private modalService = inject(NgbModal); + readonly FeatureToggle = FeatureToggle; @Input() @@ -22,11 +30,6 @@ export class ProgrammingExerciseCreateButtonsComponent { faFileImport = faFileImport; faKeyboard = faKeyboard; - constructor( - private router: Router, - private modalService: NgbModal, - ) {} - openImportModal() { const modalRef = this.modalService.open(ExerciseImportWrapperComponent, { size: 'lg', backdrop: 'static' }); modalRef.componentInstance.exerciseType = ExerciseType.PROGRAMMING; diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts index a7ae630cfa52..c3eff649807f 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-detail.component.ts @@ -1,7 +1,8 @@ -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { Component, OnDestroy, OnInit, ViewEncapsulation, inject } from '@angular/core'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { SafeHtml } from '@angular/platform-browser'; import { ProgrammingExerciseBuildConfig } from 'app/entities/programming/programming-exercise-build.config'; +import { ExerciseDetailStatisticsComponent } from 'app/exercises/shared/statistics/exercise-detail-statistics.component'; import { Subject, Subscription, of } from 'rxjs'; import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; @@ -13,7 +14,7 @@ import { ActionType } from 'app/shared/delete-dialog/delete-dialog.model'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { ExerciseType, IncludedInOverallScore } from 'app/entities/exercise.model'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { ConfirmAutofocusModalComponent } from 'app/shared/components/confirm-autofocus-modal.component'; import { TranslateService } from '@ngx-translate/core'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; @@ -58,14 +59,59 @@ import { AeolusService } from 'app/exercises/programming/shared/service/aeolus.s import { catchError, mergeMap, tap } from 'rxjs/operators'; import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; import { BuildLogStatisticsDTO } from 'app/entities/programming/build-log-statistics-dto'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { OrionFilterDirective } from 'app/shared/orion/orion-filter.directive'; +import { FeatureToggleLinkDirective } from 'app/shared/feature-toggle/feature-toggle-link.directive'; +import { ProgrammingExerciseInstructorExerciseDownloadComponent } from '../shared/actions/programming-exercise-instructor-exercise-download.component'; +import { FeatureToggleDirective } from 'app/shared/feature-toggle/feature-toggle.directive'; +import { ProgrammingExerciseResetButtonDirective } from './reset/programming-exercise-reset-button.directive'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { DetailOverviewListComponent } from 'app/detail-overview-list/detail-overview-list.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-programming-exercise-detail', templateUrl: './programming-exercise-detail.component.html', styleUrls: ['./programming-exercise-detail.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + TranslateDirective, + DocumentationButtonComponent, + RouterLink, + FaIconComponent, + OrionFilterDirective, + FeatureToggleLinkDirective, + NgbTooltip, + ProgrammingExerciseInstructorExerciseDownloadComponent, + FeatureToggleDirective, + ProgrammingExerciseResetButtonDirective, + DeleteButtonDirective, + ExerciseDetailStatisticsComponent, + DetailOverviewListComponent, + ArtemisTranslatePipe, + ], }) export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy { + private activatedRoute = inject(ActivatedRoute); + private accountService = inject(AccountService); + private programmingExerciseService = inject(ProgrammingExerciseService); + exerciseService = inject(ExerciseService); + private artemisMarkdown = inject(ArtemisMarkdownService); + private alertService = inject(AlertService); + private programmingExerciseSubmissionPolicyService = inject(SubmissionPolicyService); + private eventManager = inject(EventManager); + modalService = inject(NgbModal); + private translateService = inject(TranslateService); + private profileService = inject(ProfileService); + private statisticsService = inject(StatisticsService); + private router = inject(Router); + private programmingLanguageFeatureService = inject(ProgrammingLanguageFeatureService); + private consistencyCheckService = inject(ConsistencyCheckService); + private irisSettingsService = inject(IrisSettingsService); + private aeolusService = inject(AeolusService); + protected readonly dayjs = dayjs; protected readonly ActionType = ActionType; protected readonly ProgrammingExerciseParticipationType = ProgrammingExerciseParticipationType; @@ -131,26 +177,6 @@ export class ProgrammingExerciseDetailComponent implements OnInit, OnDestroy { exerciseDetailSections: DetailOverviewSection[]; - constructor( - private activatedRoute: ActivatedRoute, - private accountService: AccountService, - private programmingExerciseService: ProgrammingExerciseService, - public exerciseService: ExerciseService, - private artemisMarkdown: ArtemisMarkdownService, - private alertService: AlertService, - private programmingExerciseSubmissionPolicyService: SubmissionPolicyService, - private eventManager: EventManager, - public modalService: NgbModal, - private translateService: TranslateService, - private profileService: ProfileService, - private statisticsService: StatisticsService, - private router: Router, - private programmingLanguageFeatureService: ProgrammingLanguageFeatureService, - private consistencyCheckService: ConsistencyCheckService, - private irisSettingsService: IrisSettingsService, - private aeolusService: AeolusService, - ) {} - ngOnInit() { this.checkBuildPlanEditable(); diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-edit-selected.component.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise-edit-selected.component.ts index 287e50b11913..0761976562a0 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-edit-selected.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-edit-selected.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; @@ -8,12 +8,25 @@ import { ProgrammingExerciseService } from 'app/exercises/programming/manage/ser import { AlertService, AlertType } from 'app/core/util/alert.service'; import { faSave } from '@fortawesome/free-solid-svg-icons'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { ProgrammingExerciseLifecycleComponent } from '../shared/lifecycle/programming-exercise-lifecycle.component'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-programming-exercise-edit-selected', templateUrl: './programming-exercise-edit-selected.component.html', + imports: [TranslateDirective, FormsModule, HelpIconComponent, ProgrammingExerciseLifecycleComponent, ButtonComponent, FaIconComponent], }) export class ProgrammingExerciseEditSelectedComponent implements OnInit { + private activeModal = inject(NgbActiveModal); + private translateService = inject(TranslateService); + private alertService = inject(AlertService); + private programmingExerciseService = inject(ProgrammingExerciseService); + private exerciseService = inject(ExerciseService); + newProgrammingExercise: ProgrammingExercise; selectedProgrammingExercises: ProgrammingExercise[]; @@ -27,14 +40,6 @@ export class ProgrammingExerciseEditSelectedComponent implements OnInit { // Icons faSave = faSave; - constructor( - private activeModal: NgbActiveModal, - private translateService: TranslateService, - private alertService: AlertService, - private programmingExerciseService: ProgrammingExerciseService, - private exerciseService: ExerciseService, - ) {} - ngOnInit(): void { this.notificationText = undefined; this.newProgrammingExercise = new ProgrammingExercise(undefined, undefined); diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-management-routing.module.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise-management-routing.module.ts index bda9a0f7860c..c44e83c4b563 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-management-routing.module.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-management-routing.module.ts @@ -1,27 +1,20 @@ import { ActivatedRouteSnapshot, Resolve, RouterModule, Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { Injectable, NgModule } from '@angular/core'; -import { ProgrammingExerciseDetailComponent } from 'app/exercises/programming/manage/programming-exercise-detail.component'; -import { ProgrammingExerciseUpdateComponent } from 'app/exercises/programming/manage/update/programming-exercise-update.component'; +import { Injectable, NgModule, inject } from '@angular/core'; + import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { map } from 'rxjs/operators'; import { HttpResponse } from '@angular/common/http'; import { of } from 'rxjs'; -import { ProgrammingExerciseConfigureGradingComponent } from 'app/exercises/programming/manage/grading/programming-exercise-configure-grading.component'; + import { Authority } from 'app/shared/constants/authority.constants'; -import { PlagiarismInspectorComponent } from 'app/exercises/shared/plagiarism/plagiarism-inspector/plagiarism-inspector.component'; -import { ExerciseStatisticsComponent } from 'app/exercises/shared/statistics/exercise-statistics.component'; -import { BuildPlanEditorComponent } from 'app/exercises/programming/manage/build-plan-editor.component'; -import { RepositoryViewComponent } from 'app/localvc/repository-view/repository-view.component'; -import { CommitHistoryComponent } from 'app/localvc/commit-history/commit-history.component'; -import { CommitDetailsViewComponent } from 'app/localvc/commit-details-view/commit-details-view.component'; + import { LocalVCGuard } from 'app/localvc/localvc-guard.service'; -import { VcsRepositoryAccessLogViewComponent } from 'app/localvc/vcs-repository-access-log-view/vcs-repository-access-log-view.component'; @Injectable({ providedIn: 'root' }) export class ProgrammingExerciseResolve implements Resolve { - constructor(private service: ProgrammingExerciseService) {} + private service = inject(ProgrammingExerciseService); resolve(route: ActivatedRouteSnapshot) { const exerciseId = route.params['exerciseId'] ? route.params['exerciseId'] : undefined; @@ -35,7 +28,7 @@ export class ProgrammingExerciseResolve implements Resolve export const routes: Routes = [ { path: ':courseId/programming-exercises/new', - component: ProgrammingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/programming/manage/update/programming-exercise-update.component').then((m) => m.ProgrammingExerciseUpdateComponent), resolve: { programmingExercise: ProgrammingExerciseResolve, }, @@ -47,7 +40,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/edit', - component: ProgrammingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/programming/manage/update/programming-exercise-update.component').then((m) => m.ProgrammingExerciseUpdateComponent), resolve: { programmingExercise: ProgrammingExerciseResolve, }, @@ -59,7 +52,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/import/:exerciseId', - component: ProgrammingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/programming/manage/update/programming-exercise-update.component').then((m) => m.ProgrammingExerciseUpdateComponent), resolve: { programmingExercise: ProgrammingExerciseResolve, }, @@ -71,7 +64,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/import-from-file', - component: ProgrammingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/programming/manage/update/programming-exercise-update.component').then((m) => m.ProgrammingExerciseUpdateComponent), resolve: { programmingExercise: ProgrammingExerciseResolve, }, @@ -83,7 +76,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId', - component: ProgrammingExerciseDetailComponent, + loadComponent: () => import('app/exercises/programming/manage/programming-exercise-detail.component').then((m) => m.ProgrammingExerciseDetailComponent), resolve: { programmingExercise: ProgrammingExerciseResolve, }, @@ -95,7 +88,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/plagiarism', - component: PlagiarismInspectorComponent, + loadComponent: () => import('app/exercises/shared/plagiarism/plagiarism-inspector/plagiarism-inspector.component').then((m) => m.PlagiarismInspectorComponent), resolve: { exercise: ProgrammingExerciseResolve, }, @@ -107,7 +100,8 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/grading/:tab', - component: ProgrammingExerciseConfigureGradingComponent, + loadComponent: () => + import('app/exercises/programming/manage/grading/programming-exercise-configure-grading.component').then((m) => m.ProgrammingExerciseConfigureGradingComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.programmingExercise.home.title', @@ -120,7 +114,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/exercise-statistics', - component: ExerciseStatisticsComponent, + loadComponent: () => import('app/exercises/shared/statistics/exercise-statistics.component').then((m) => m.ExerciseStatisticsComponent), resolve: { exercise: ProgrammingExerciseResolve, }, @@ -137,7 +131,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/edit-build-plan', - component: BuildPlanEditorComponent, + loadComponent: () => import('app/exercises/programming/manage/build-plan-editor.component').then((m) => m.BuildPlanEditorComponent), resolve: { exercise: ProgrammingExerciseResolve, }, @@ -149,7 +143,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/repository/:repositoryType', - component: RepositoryViewComponent, + loadComponent: () => import('app/localvc/repository-view/repository-view.component').then((m) => m.RepositoryViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -158,7 +152,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/repository/:repositoryType/repo/:repositoryId', - component: RepositoryViewComponent, + loadComponent: () => import('app/localvc/repository-view/repository-view.component').then((m) => m.RepositoryViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -167,7 +161,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/repository/:repositoryType/commit-history', - component: CommitHistoryComponent, + loadComponent: () => import('app/localvc/commit-history/commit-history.component').then((m) => m.CommitHistoryComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR], pageTitle: 'artemisApp.repository.title', @@ -176,7 +170,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/repository/:repositoryType/repo/:repositoryId/commit-history', - component: CommitHistoryComponent, + loadComponent: () => import('app/localvc/commit-history/commit-history.component').then((m) => m.CommitHistoryComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR], pageTitle: 'artemisApp.repository.title', @@ -185,7 +179,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/repository/:repositoryType/vcs-access-log', - component: VcsRepositoryAccessLogViewComponent, + loadComponent: () => import('app/localvc/vcs-repository-access-log-view/vcs-repository-access-log-view.component').then((m) => m.VcsRepositoryAccessLogViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.repository.title', @@ -194,7 +188,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/repository/:repositoryType/repo/:repositoryId/vcs-access-log', - component: VcsRepositoryAccessLogViewComponent, + loadComponent: () => import('app/localvc/vcs-repository-access-log-view/vcs-repository-access-log-view.component').then((m) => m.VcsRepositoryAccessLogViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.repository.title', @@ -203,7 +197,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/repository/:repositoryType/commit-history/:commitHash', - component: CommitDetailsViewComponent, + loadComponent: () => import('app/localvc/commit-details-view/commit-details-view.component').then((m) => m.CommitDetailsViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR], pageTitle: 'artemisApp.repository.title', @@ -212,7 +206,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/participations/:participationId/repository', - component: RepositoryViewComponent, + loadComponent: () => import('app/localvc/repository-view/repository-view.component').then((m) => m.RepositoryViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -221,7 +215,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/participations/:participationId/repository/commit-history', - component: CommitHistoryComponent, + loadComponent: () => import('app/localvc/commit-history/commit-history.component').then((m) => m.CommitHistoryComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -230,7 +224,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/participations/:participationId/repository/vcs-access-log', - component: VcsRepositoryAccessLogViewComponent, + loadComponent: () => import('app/localvc/vcs-repository-access-log-view/vcs-repository-access-log-view.component').then((m) => m.VcsRepositoryAccessLogViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.repository.title', @@ -239,7 +233,7 @@ export const routes: Routes = [ }, { path: ':courseId/programming-exercises/:exerciseId/participations/:participationId/repository/commit-history/:commitHash', - component: CommitDetailsViewComponent, + loadComponent: () => import('app/localvc/commit-details-view/commit-details-view.component').then((m) => m.CommitDetailsViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise-management.module.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise-management.module.ts index d7aa345e3758..a0b4f6cf625d 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise-management.module.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise-management.module.ts @@ -10,7 +10,7 @@ import { ArtemisProgrammingExerciseInstructionsEditorModule } from 'app/exercise import { ArtemisProgrammingExerciseUpdateModule } from 'app/exercises/programming/manage/update/programming-exercise-update.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ArtemisProgrammingExerciseStatusModule } from 'app/exercises/programming/manage/status/programming-exercise-status.module'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { ArtemisProgrammingExerciseModule } from 'app/exercises/programming/shared/programming-exercise.module'; import { AssessmentInstructionsModule } from 'app/assessment/assessment-instructions/assessment-instructions.module'; import { ProgrammingExerciseEditSelectedComponent } from 'app/exercises/programming/manage/programming-exercise-edit-selected.component'; @@ -42,7 +42,6 @@ import { CodeEditorHeaderComponent } from 'app/exercises/programming/shared/code ArtemisProgrammingExerciseInstructionsEditorModule, ArtemisProgrammingExerciseUpdateModule, ArtemisProgrammingExerciseStatusModule, - FeatureToggleModule, AssessmentInstructionsModule, OrionModule, ArtemisProgrammingExerciseLifecycleModule, @@ -53,8 +52,6 @@ import { CodeEditorHeaderComponent } from 'app/exercises/programming/shared/code IrisModule, MonacoEditorComponent, CodeEditorHeaderComponent, - ], - declarations: [ ProgrammingExerciseDetailComponent, ProgrammingExerciseEditSelectedComponent, ProgrammingExerciseInstructorExerciseDownloadComponent, diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.html b/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.html index fd15337f32c2..3b507c6750dd 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.html +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.html @@ -16,10 +16,11 @@

    - - @if (!localVCEnabled) { - - } + + + + + @@ -76,7 +77,9 @@ - @if (!localVCEnabled) { - - @@ -624,39 +637,40 @@

    - @if (submissionWithComplaint.complaint.accepted === undefined) { - - - {{ 'artemisApp.exerciseAssessmentDashboard.evaluateComplaint' | artemisTranslate }} - - } @else { - - - {{ 'artemisApp.exerciseAssessmentDashboard.showComplaint' | artemisTranslate }} - - } - + + + + + + + + + + + + + @if (submissionWithComplaint.complaint.accepted === undefined) { + + + {{ 'artemisApp.exerciseAssessmentDashboard.evaluateComplaint' | artemisTranslate }} + + } @else { + + + {{ 'artemisApp.exerciseAssessmentDashboard.showComplaint' | artemisTranslate }} + + } + } @@ -736,40 +750,41 @@

    - @if (moreFeedbackRequest.complaint.accepted === undefined) { - - - {{ 'artemisApp.exerciseAssessmentDashboard.evaluateMoreFeedbackRequest' | artemisTranslate }} - - } @else { - - } - + + + + + + + + + + + + + + + @if (moreFeedbackRequest.complaint.accepted === undefined) { + + + {{ 'artemisApp.exerciseAssessmentDashboard.evaluateMoreFeedbackRequest' | artemisTranslate }} + + } @else { + + } + } diff --git a/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.ts b/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.ts index 63a23747c1f9..a7fa7728e0bc 100644 --- a/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.ts +++ b/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.ts @@ -1,10 +1,12 @@ -import { Component, ContentChild, OnInit, TemplateRef } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { SafeHtml } from '@angular/platform-browser'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { AlertService } from 'app/core/util/alert.service'; import { User } from 'app/core/user/user.model'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; +import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; +import { ProgrammingExerciseInstructionComponent } from 'app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component'; import { TutorParticipationService } from 'app/exercises/shared/dashboards/tutor/tutor-participation.service'; import { TextSubmissionService } from 'app/exercises/text/participate/text-submission.service'; import { ExampleSubmission } from 'app/entities/example-submission.model'; @@ -38,9 +40,9 @@ import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; import { SortService } from 'app/shared/service/sort.service'; import { onError } from 'app/shared/util/global.utils'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; -import { ArtemisNavigationUtilService, getLinkToSubmissionAssessment } from 'app/utils/navigation.utils'; +import { getLinkToSubmissionAssessment } from 'app/utils/navigation.utils'; import { AssessmentType } from 'app/entities/assessment-type.model'; -import { LegendPosition } from '@swimlane/ngx-charts'; +import { LegendPosition, PieChartModule } from '@swimlane/ngx-charts'; import { AssessmentDashboardInformationEntry } from 'app/course/dashboards/assessment-dashboard/assessment-dashboard-information.component'; import dayjs from 'dayjs/esm'; import { faCheckCircle, faExclamationTriangle, faFolderOpen, faListAlt, faQuestionCircle, faSort, faSpinner } from '@fortawesome/free-solid-svg-icons'; @@ -48,6 +50,28 @@ import { GraphColors } from 'app/entities/statistics.model'; import { PROFILE_LOCALVC } from 'app/app.constants'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { isManualResult } from 'app/exercises/shared/result/result.utils'; +import { HeaderExercisePageWithDetailsComponent } from '../../exercise-headers/header-exercise-page-with-details.component'; +import { TutorParticipationGraphComponent } from 'app/shared/dashboards/tutor-participation-graph/tutor-participation-graph.component'; +import { SecondCorrectionEnableButtonComponent } from './second-correction-button/second-correction-enable-button.component'; +import { SidePanelComponent } from 'app/shared/side-panel/side-panel.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { InfoPanelComponent } from 'app/shared/info-panel/info-panel.component'; +import { SecureLinkDirective } from 'app/shared/http/secure-link.directive'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { CodeButtonComponent } from 'app/shared/components/code-button/code-button.component'; +import { StructuredGradingInstructionsAssessmentLayoutComponent } from 'app/assessment/structured-grading-instructions-assessment-layout/structured-grading-instructions-assessment-layout.component'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { LanguageTableCellComponent } from './language-table-cell/language-table-cell.component'; +import { NgStyle } from '@angular/common'; +import { ResultComponent } from '../../result/result.component'; +import { AssessmentWarningComponent } from 'app/assessment/assessment-warning/assessment-warning.component'; +import { CollapsableAssessmentInstructionsComponent } from 'app/assessment/assessment-instructions/collapsable-assessment-instructions/collapsable-assessment-instructions.component'; +import { TutorLeaderboardComponent } from 'app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; export interface ExampleSubmissionQueryParams { readOnly?: boolean; @@ -59,10 +83,60 @@ export interface ExampleSubmissionQueryParams { templateUrl: './exercise-assessment-dashboard.component.html', styleUrls: ['./exercise-assessment-dashboard.component.scss'], providers: [CourseManagementService], + imports: [ + HeaderExercisePageWithDetailsComponent, + TutorParticipationGraphComponent, + SecondCorrectionEnableButtonComponent, + PieChartModule, + SidePanelComponent, + TranslateDirective, + RouterLink, + FaIconComponent, + InfoPanelComponent, + ProgrammingExerciseInstructionComponent, + ModelingEditorComponent, + SecureLinkDirective, + ButtonComponent, + CodeButtonComponent, + StructuredGradingInstructionsAssessmentLayoutComponent, + NgbTooltip, + SortDirective, + SortByDirective, + LanguageTableCellComponent, + NgStyle, + ResultComponent, + AssessmentWarningComponent, + CollapsableAssessmentInstructionsComponent, + TutorLeaderboardComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + ArtemisDurationFromSecondsPipe, + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // ExtensionPointDirective, + ], }) export class ExerciseAssessmentDashboardComponent implements OnInit { + complaintService = inject(ComplaintService); + private exerciseService = inject(ExerciseService); + private alertService = inject(AlertService); + private translateService = inject(TranslateService); + private accountService = inject(AccountService); + private route = inject(ActivatedRoute); + private tutorParticipationService = inject(TutorParticipationService); + private submissionService = inject(SubmissionService); + private textSubmissionService = inject(TextSubmissionService); + private modelingSubmissionService = inject(ModelingSubmissionService); + private fileUploadSubmissionService = inject(FileUploadSubmissionService); + private artemisMarkdown = inject(ArtemisMarkdownService); + private router = inject(Router); + private programmingSubmissionService = inject(ProgrammingSubmissionService); + private guidedTourService = inject(GuidedTourService); + private sortService = inject(SortService); + private profileService = inject(ProfileService); + readonly roundScoreSpecifiedByCourseSettings = roundValueSpecifiedByCourseSettings; readonly getCourseFromExercise = getCourseFromExercise; + exercise: Exercise; modelingExercise: ModelingExercise; programmingExercise: ProgrammingExercise; @@ -111,7 +185,6 @@ export class ExerciseAssessmentDashboardComponent implements OnInit { getSubmissionResultByCorrectionRound = getSubmissionResultByCorrectionRound; // helper variables to display information message about why no new assessments are possible anymore - remainingAssessments: number[] = []; lockedSubmissionsByOtherTutor: number[] = []; notYetAssessed: number[] = []; firstRoundAssessments: number; @@ -159,8 +232,9 @@ export class ExerciseAssessmentDashboardComponent implements OnInit { moreFeedbackRequestsLink: any[]; // extension points, see shared/extension-point - @ContentChild('overrideAssessmentTable') overrideAssessmentTable: TemplateRef; - @ContentChild('overrideOpenAssessmentButton') overrideOpenAssessmentButton: TemplateRef; + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution --> + // @ContentChild('overrideAssessmentTable') overrideAssessmentTable: TemplateRef; + // @ContentChild('overrideOpenAssessmentButton') overrideOpenAssessmentButton: TemplateRef; // Icons faSpinner = faSpinner; @@ -171,28 +245,6 @@ export class ExerciseAssessmentDashboardComponent implements OnInit { faExclamationTriangle = faExclamationTriangle; faListAlt = faListAlt; - constructor( - public complaintService: ComplaintService, - private exerciseService: ExerciseService, - private alertService: AlertService, - private translateService: TranslateService, - private accountService: AccountService, - private route: ActivatedRoute, - private tutorParticipationService: TutorParticipationService, - private submissionService: SubmissionService, - private textSubmissionService: TextSubmissionService, - private modelingSubmissionService: ModelingSubmissionService, - private fileUploadSubmissionService: FileUploadSubmissionService, - private artemisMarkdown: ArtemisMarkdownService, - private router: Router, - private programmingSubmissionService: ProgrammingSubmissionService, - private guidedTourService: GuidedTourService, - private artemisDatePipe: ArtemisDatePipe, - private sortService: SortService, - private navigationUtilService: ArtemisNavigationUtilService, - private profileService: ProfileService, - ) {} - /** * Extracts the course and exercise ids from the route params and fetches the exercise from the server */ diff --git a/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.module.ts b/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.module.ts index 24a3fa815594..307c0c19dea2 100644 --- a/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.module.ts +++ b/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.module.ts @@ -45,8 +45,11 @@ const ENTITY_STATES = [...exerciseAssessmentDashboardRoute]; OrionModule, SubmissionResultStatusModule, PieChartModule, + ExerciseAssessmentDashboardComponent, + OrionExerciseAssessmentDashboardComponent, + SecondCorrectionEnableButtonComponent, + LanguageTableCellComponent, ], - declarations: [ExerciseAssessmentDashboardComponent, OrionExerciseAssessmentDashboardComponent, SecondCorrectionEnableButtonComponent, LanguageTableCellComponent], providers: [], exports: [SecondCorrectionEnableButtonComponent, LanguageTableCellComponent], }) diff --git a/src/main/webapp/app/exercises/shared/dashboards/tutor/language-table-cell/language-table-cell.component.ts b/src/main/webapp/app/exercises/shared/dashboards/tutor/language-table-cell/language-table-cell.component.ts index 96741ee28f37..014fe88c9d5a 100644 --- a/src/main/webapp/app/exercises/shared/dashboards/tutor/language-table-cell/language-table-cell.component.ts +++ b/src/main/webapp/app/exercises/shared/dashboards/tutor/language-table-cell/language-table-cell.component.ts @@ -1,10 +1,12 @@ import { Component, Input } from '@angular/core'; import { Submission } from 'app/entities/submission.model'; import { TextSubmission } from 'app/entities/text/text-submission.model'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-language-table-cell', template: "{{ 'artemisApp.exerciseAssessmentDashboard.languages.' + (textSubmission.language || 'UNKNOWN') | artemisTranslate }}", + imports: [ArtemisTranslatePipe], }) export class LanguageTableCellComponent { textSubmission: TextSubmission; diff --git a/src/main/webapp/app/exercises/shared/dashboards/tutor/second-correction-button/second-correction-enable-button.component.ts b/src/main/webapp/app/exercises/shared/dashboards/tutor/second-correction-button/second-correction-enable-button.component.ts index 2e5a8cfb2677..643c3dc9d671 100644 --- a/src/main/webapp/app/exercises/shared/dashboards/tutor/second-correction-button/second-correction-enable-button.component.ts +++ b/src/main/webapp/app/exercises/shared/dashboards/tutor/second-correction-button/second-correction-enable-button.component.ts @@ -1,10 +1,13 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { faSpinner, faToggleOff, faToggleOn } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-second-correction-enable-button', templateUrl: './second-correction-enable-button.component.html', styles: ['div { cursor: pointer; }'], + imports: [FaIconComponent, ArtemisTranslatePipe], }) export class SecondCorrectionEnableButtonComponent { @Input() secondCorrectionEnabled: boolean; diff --git a/src/main/webapp/app/exercises/shared/dashboards/tutor/tutor-participation.service.ts b/src/main/webapp/app/exercises/shared/dashboards/tutor/tutor-participation.service.ts index 81d63ac07188..739a49c77819 100644 --- a/src/main/webapp/app/exercises/shared/dashboards/tutor/tutor-participation.service.ts +++ b/src/main/webapp/app/exercises/shared/dashboards/tutor/tutor-participation.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { ExampleSubmission } from 'app/entities/example-submission.model'; @@ -12,12 +12,10 @@ export type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class TutorParticipationService { - public resourceUrl = 'api/exercises'; + private http = inject(HttpClient); + private accountService = inject(AccountService); - constructor( - private http: HttpClient, - private accountService: AccountService, - ) {} + public resourceUrl = 'api/exercises'; /** * Starts the exercise with the given ID for the current tutor. A tutor participation will be created and returned diff --git a/src/main/webapp/app/exercises/shared/difficulty-picker/difficulty-picker.component.ts b/src/main/webapp/app/exercises/shared/difficulty-picker/difficulty-picker.component.ts index d3b785e821d0..8da18d7d5c14 100644 --- a/src/main/webapp/app/exercises/shared/difficulty-picker/difficulty-picker.component.ts +++ b/src/main/webapp/app/exercises/shared/difficulty-picker/difficulty-picker.component.ts @@ -1,10 +1,13 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { DifficultyLevel, Exercise } from 'app/entities/exercise.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass } from '@angular/common'; @Component({ selector: 'jhi-difficulty-picker', templateUrl: './difficulty-picker.component.html', styles: ['div { cursor: pointer; }'], + imports: [TranslateDirective, NgClass], }) export class DifficultyPickerComponent { readonly DifficultyLevel = DifficultyLevel; diff --git a/src/main/webapp/app/exercises/shared/difficulty-picker/difficulty-picker.module.ts b/src/main/webapp/app/exercises/shared/difficulty-picker/difficulty-picker.module.ts index 091ab70044f9..68e2b7ba16aa 100644 --- a/src/main/webapp/app/exercises/shared/difficulty-picker/difficulty-picker.module.ts +++ b/src/main/webapp/app/exercises/shared/difficulty-picker/difficulty-picker.module.ts @@ -4,8 +4,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { DifficultyPickerComponent } from 'app/exercises/shared/difficulty-picker/difficulty-picker.component'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [DifficultyPickerComponent], + imports: [ArtemisSharedModule, DifficultyPickerComponent], exports: [DifficultyPickerComponent], }) export class ArtemisDifficultyPickerModule {} diff --git a/src/main/webapp/app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component.ts b/src/main/webapp/app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component.ts index 49a62930183d..66984147498e 100644 --- a/src/main/webapp/app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component.ts +++ b/src/main/webapp/app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { Subject } from 'rxjs'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; @@ -16,12 +16,27 @@ import { faBook, faExclamationTriangle, faEye, faFileExport, faFileSignature, fa import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { PROFILE_LOCALCI, PROFILE_LOCALVC } from 'app/app.constants'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exam-exercise-row-buttons', templateUrl: './exam-exercise-row-buttons.component.html', + imports: [RouterLink, FaIconComponent, TranslateDirective, NgbTooltip, DeleteButtonDirective, ArtemisTranslatePipe], }) export class ExamExerciseRowButtonsComponent implements OnInit { + private textExerciseService = inject(TextExerciseService); + private fileUploadExerciseService = inject(FileUploadExerciseService); + private programmingExerciseService = inject(ProgrammingExerciseService); + private modelingExerciseService = inject(ModelingExerciseService); + private quizExerciseService = inject(QuizExerciseService); + private eventManager = inject(EventManager); + private profileService = inject(ProfileService); + @Input() course: Course; @Input() exercise: Exercise; @Input() exam: Exam; @@ -49,16 +64,6 @@ export class ExamExerciseRowButtonsComponent implements OnInit { localVCEnabled = false; localCIEnabled = false; - constructor( - private textExerciseService: TextExerciseService, - private fileUploadExerciseService: FileUploadExerciseService, - private programmingExerciseService: ProgrammingExerciseService, - private modelingExerciseService: ModelingExerciseService, - private quizExerciseService: QuizExerciseService, - private eventManager: EventManager, - private profileService: ProfileService, - ) {} - ngOnInit(): void { this.profileService.getProfileInfo().subscribe((profileInfo) => { this.localVCEnabled = profileInfo.activeProfiles.includes(PROFILE_LOCALVC); diff --git a/src/main/webapp/app/exercises/shared/example-solution/example-solution.component.ts b/src/main/webapp/app/exercises/shared/example-solution/example-solution.component.ts index 803dc8b97127..6afa48f071f6 100644 --- a/src/main/webapp/app/exercises/shared/example-solution/example-solution.component.ts +++ b/src/main/webapp/app/exercises/shared/example-solution/example-solution.component.ts @@ -1,27 +1,31 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; import { ActivatedRoute } from '@angular/router'; import { Exercise } from 'app/entities/exercise.model'; import { ExampleSolutionInfo, ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; +import { HeaderExercisePageWithDetailsComponent } from '../exercise-headers/header-exercise-page-with-details.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ModelingEditorComponent } from '../../modeling/shared/modeling-editor.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-example-solution', templateUrl: './example-solution.component.html', + imports: [HeaderExercisePageWithDetailsComponent, TranslateDirective, ModelingEditorComponent, ArtemisTranslatePipe, HtmlForMarkdownPipe], }) export class ExampleSolutionComponent implements OnInit { + private exerciseService = inject(ExerciseService); + private route = inject(ActivatedRoute); + private artemisMarkdown = inject(ArtemisMarkdownService); + private displayedExerciseId: number; public exercise?: Exercise; public exampleSolutionInfo?: ExampleSolutionInfo; @Input() exerciseId?: number; - @Input() displayHeader?: boolean = true; - - constructor( - private exerciseService: ExerciseService, - private route: ActivatedRoute, - private artemisMarkdown: ArtemisMarkdownService, - ) {} + @Input() displayHeader = true; ngOnInit() { this.route.params.subscribe((params) => { diff --git a/src/main/webapp/app/exercises/shared/example-submission/example-submission-import/example-submission-import-paging.service.ts b/src/main/webapp/app/exercises/shared/example-submission/example-submission-import/example-submission-import-paging.service.ts index 4b5424ee23a8..24f479957a54 100644 --- a/src/main/webapp/app/exercises/shared/example-submission/example-submission-import/example-submission-import-paging.service.ts +++ b/src/main/webapp/app/exercises/shared/example-submission/example-submission-import/example-submission-import-paging.service.ts @@ -1,5 +1,5 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Submission } from 'app/entities/submission.model'; import { PagingService } from 'app/exercises/shared/manage/paging.service'; import { SearchResult, SearchTermPageableSearch } from 'app/shared/table/pageable-table'; @@ -10,9 +10,11 @@ type EntityResponseType = SearchResult; @Injectable({ providedIn: 'root' }) export class ExampleSubmissionImportPagingService extends PagingService { + private http = inject(HttpClient); + private static readonly RESOURCE_URL = 'api/exercises'; - constructor(private http: HttpClient) { + constructor() { super(); } diff --git a/src/main/webapp/app/exercises/shared/example-submission/example-submission-import/example-submission-import.component.ts b/src/main/webapp/app/exercises/shared/example-submission/example-submission-import/example-submission-import.component.ts index 635fe6c1e8d1..41e74a42580c 100644 --- a/src/main/webapp/app/exercises/shared/example-submission/example-submission-import/example-submission-import.component.ts +++ b/src/main/webapp/app/exercises/shared/example-submission/example-submission-import/example-submission-import.component.ts @@ -1,32 +1,49 @@ -import { Component } from '@angular/core'; -import { Router } from '@angular/router'; +import { Component, inject } from '@angular/core'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { Submission } from 'app/entities/submission.model'; -import { ExampleSubmissionImportPagingService } from 'app/exercises/shared/example-submission/example-submission-import/example-submission-import-paging.service'; import { ExampleSubmissionService } from 'app/exercises/shared/example-submission/example-submission.service'; import { ImportComponent } from 'app/shared/import/import.component'; -import { SortService } from 'app/shared/service/sort.service'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbPagination, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ResultComponent } from '../../result/result.component'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ExampleSubmissionImportPagingService } from 'app/exercises/shared/example-submission/example-submission-import/example-submission-import-paging.service'; @Component({ selector: 'jhi-example-submission-import', templateUrl: './example-submission-import.component.html', + imports: [ + FormsModule, + TranslateDirective, + SortDirective, + SortByDirective, + FaIconComponent, + NgbTooltip, + ResultComponent, + ButtonComponent, + NgbPagination, + ArtemisDatePipe, + ArtemisTranslatePipe, + ], }) export class ExampleSubmissionImportComponent extends ImportComponent { + private exampleSubmissionService = inject(ExampleSubmissionService); + exercise: Exercise; readonly faQuestionCircle = faQuestionCircle; readonly ExerciseType = ExerciseType; - constructor( - router: Router, - sortService: SortService, - activeModal: NgbActiveModal, - pagingService: ExampleSubmissionImportPagingService, - private exampleSubmissionService: ExampleSubmissionService, - ) { - super(router, sortService, activeModal, pagingService); + constructor() { + const pagingService = inject(ExampleSubmissionImportPagingService); + super(pagingService); } get searchTermEntered() { diff --git a/src/main/webapp/app/exercises/shared/example-submission/example-submission.service.ts b/src/main/webapp/app/exercises/shared/example-submission/example-submission.service.ts index 5fe845bfe309..a170773c1ace 100644 --- a/src/main/webapp/app/exercises/shared/example-submission/example-submission.service.ts +++ b/src/main/webapp/app/exercises/shared/example-submission/example-submission.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { ExampleSubmission } from 'app/entities/example-submission.model'; @@ -14,10 +14,8 @@ export type EntityResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class ExampleSubmissionService { - constructor( - private http: HttpClient, - private stringCountService: StringCountService, - ) {} + private http = inject(HttpClient); + private stringCountService = inject(StringCountService); /** * Creates an example submission diff --git a/src/main/webapp/app/exercises/shared/example-submission/example-submissions.component.ts b/src/main/webapp/app/exercises/shared/example-submission/example-submissions.component.ts index 6c1eec63381f..e8e8995b4290 100644 --- a/src/main/webapp/app/exercises/shared/example-submission/example-submissions.component.ts +++ b/src/main/webapp/app/exercises/shared/example-submission/example-submissions.component.ts @@ -1,21 +1,33 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { HttpErrorResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; import { ExampleSubmissionService } from 'app/exercises/shared/example-submission/example-submission.service'; import { Exercise, ExerciseType, getCourseFromExercise } from 'app/entities/exercise.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { ExampleSubmissionImportComponent } from 'app/exercises/shared/example-submission/example-submission-import/example-submission-import.component'; import { Submission } from 'app/entities/submission.model'; import { onError } from 'app/shared/util/global.utils'; import { AccountService } from 'app/core/auth/account.service'; import { faExclamationTriangle, faFont, faPlus, faQuestionCircle, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ResultComponent } from '../result/result.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ templateUrl: 'example-submissions.component.html', + imports: [TranslateDirective, RouterLink, FaIconComponent, NgbTooltip, ResultComponent, ArtemisTranslatePipe], }) export class ExampleSubmissionsComponent implements OnInit, OnDestroy { + private alertService = inject(AlertService); + private exampleSubmissionService = inject(ExampleSubmissionService); + private activatedRoute = inject(ActivatedRoute); + private courseService = inject(CourseManagementService); + private modalService = inject(NgbModal); + private accountService = inject(AccountService); + exercise: Exercise; readonly exerciseType = ExerciseType; createdExampleAssessment: boolean[]; @@ -27,15 +39,6 @@ export class ExampleSubmissionsComponent implements OnInit, OnDestroy { faQuestionCircle = faQuestionCircle; faExclamationTriangle = faExclamationTriangle; - constructor( - private alertService: AlertService, - private exampleSubmissionService: ExampleSubmissionService, - private activatedRoute: ActivatedRoute, - private courseService: CourseManagementService, - private modalService: NgbModal, - private accountService: AccountService, - ) {} - /** * Initializes all relevant data for the exercise */ diff --git a/src/main/webapp/app/exercises/shared/example-submission/example-submissions.module.ts b/src/main/webapp/app/exercises/shared/example-submission/example-submissions.module.ts index e6e936f1f64b..3c7c204d1417 100644 --- a/src/main/webapp/app/exercises/shared/example-submission/example-submissions.module.ts +++ b/src/main/webapp/app/exercises/shared/example-submission/example-submissions.module.ts @@ -13,7 +13,14 @@ import { SubmissionResultStatusModule } from 'app/overview/submission-result-sta const ENTITY_STATES = [...textExerciseRoutes, ...modelingExerciseRoutes]; @NgModule({ - imports: [ArtemisSharedModule, RouterModule.forChild(ENTITY_STATES), ArtemisSharedComponentModule, ArtemisResultModule, SubmissionResultStatusModule], - declarations: [ExampleSubmissionsComponent, ExampleSubmissionImportComponent], + imports: [ + ArtemisSharedModule, + RouterModule.forChild(ENTITY_STATES), + ArtemisSharedComponentModule, + ArtemisResultModule, + SubmissionResultStatusModule, + ExampleSubmissionsComponent, + ExampleSubmissionImportComponent, + ], }) export class ExampleSubmissionsModule {} diff --git a/src/main/webapp/app/exercises/shared/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.component.ts b/src/main/webapp/app/exercises/shared/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.component.ts index 93f06518d4e8..0836e4ccd797 100644 --- a/src/main/webapp/app/exercises/shared/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { Subject } from 'rxjs'; import { TextExerciseService } from 'app/exercises/text/manage/text-exercise/text-exercise.service'; @@ -6,18 +6,31 @@ import { HttpErrorResponse } from '@angular/common/http'; import { FileUploadExerciseService } from 'app/exercises/file-upload/manage/file-upload-exercise.service'; import { ModelingExerciseService } from 'app/exercises/modeling/manage/modeling-exercise.service'; import { Course } from 'app/entities/course.model'; -import { Router } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { EventManager } from 'app/core/util/event-manager.service'; import { faBook, faChartBar, faListAlt, faRobot, faTable, faTrash, faUserCheck, faUsers, faWrench } from '@fortawesome/free-solid-svg-icons'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { PROFILE_IRIS } from 'app/app.constants'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-non-programming-exercise-detail-common-actions', templateUrl: './non-programming-exercise-detail-common-actions.component.html', + imports: [RouterLink, FaIconComponent, TranslateDirective, NgbTooltip, DeleteButtonDirective, ArtemisTranslatePipe], }) export class NonProgrammingExerciseDetailCommonActionsComponent implements OnInit { + private textExerciseService = inject(TextExerciseService); + private fileUploadExerciseService = inject(FileUploadExerciseService); + private modelingExerciseService = inject(ModelingExerciseService); + private profileService = inject(ProfileService); + private eventManager = inject(EventManager); + private router = inject(Router); + @Input() exercise: Exercise; @@ -48,15 +61,6 @@ export class NonProgrammingExerciseDetailCommonActionsComponent implements OnIni faUserCheck = faUserCheck; faRobot = faRobot; - constructor( - private textExerciseService: TextExerciseService, - private fileUploadExerciseService: FileUploadExerciseService, - private modelingExerciseService: ModelingExerciseService, - private profileService: ProfileService, - private eventManager: EventManager, - private router: Router, - ) {} - ngOnInit(): void { if (!this.isExamExercise) { this.baseResource = `/course-management/${this.course.id!}/${this.exercise.type}-exercises/${this.exercise.id}/`; diff --git a/src/main/webapp/app/exercises/shared/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.module.ts b/src/main/webapp/app/exercises/shared/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.module.ts index 66fe3ba3703c..feee6d25e7b8 100644 --- a/src/main/webapp/app/exercises/shared/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.module.ts +++ b/src/main/webapp/app/exercises/shared/exercise-detail-common-actions/non-programming-exercise-detail-common-actions.module.ts @@ -7,8 +7,14 @@ import { RouterModule } from '@angular/router'; import { ArtemisAssessmentSharedModule } from 'app/assessment/assessment-shared.module'; @NgModule({ - declarations: [NonProgrammingExerciseDetailCommonActionsComponent], exports: [NonProgrammingExerciseDetailCommonActionsComponent], - imports: [ArtemisSharedCommonModule, ArtemisExerciseScoresModule, ArtemisSharedModule, RouterModule, ArtemisAssessmentSharedModule], + imports: [ + ArtemisSharedCommonModule, + ArtemisExerciseScoresModule, + ArtemisSharedModule, + RouterModule, + ArtemisAssessmentSharedModule, + NonProgrammingExerciseDetailCommonActionsComponent, + ], }) export class NonProgrammingExerciseDetailCommonActionsModule {} diff --git a/src/main/webapp/app/exercises/shared/exercise-headers/difficulty-badge.component.ts b/src/main/webapp/app/exercises/shared/exercise-headers/difficulty-badge.component.ts index 89a305b9d378..5f79b045566b 100644 --- a/src/main/webapp/app/exercises/shared/exercise-headers/difficulty-badge.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-headers/difficulty-badge.component.ts @@ -1,21 +1,23 @@ -import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnDestroy, OnInit, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; import { DifficultyLevel, Exercise } from 'app/entities/exercise.model'; +import { NgClass } from '@angular/common'; @Component({ selector: 'jhi-difficulty-badge', templateUrl: './difficulty-badge.component.html', + imports: [NgClass], }) export class DifficultyBadgeComponent implements OnInit, OnDestroy, OnChanges { + private translateService = inject(TranslateService); + @Input() exercise: Exercise; @Input() showNoLevel: boolean; public translatedDifficulty: string; public badgeClass: string; private translateSubscription: Subscription; - constructor(private translateService: TranslateService) {} - /** * Sets the badge attributes based on the exercise difficulty */ @@ -25,7 +27,7 @@ export class DifficultyBadgeComponent implements OnInit, OnDestroy, OnChanges { }); } - ngOnChanges(): void { + ngOnChanges() { this.setBadgeAttributes(); } diff --git a/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers-information/exercise-headers-information.component.ts b/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers-information/exercise-headers-information.component.ts index 57c245a224fa..50747da936a4 100644 --- a/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers-information/exercise-headers-information.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers-information/exercise-headers-information.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnInit, inject } from '@angular/core'; import { SortService } from 'app/shared/service/sort.service'; import dayjs from 'dayjs/esm'; import { Exercise, IncludedInOverallScore, getCourseFromExercise } from 'app/entities/exercise.model'; @@ -22,7 +22,6 @@ import { ArtemisServerDateService } from 'app/shared/server-date.service'; @Component({ selector: 'jhi-exercise-headers-information', templateUrl: './exercise-headers-information.component.html', - standalone: true, imports: [SubmissionResultStatusModule, ExerciseCategoriesModule, InformationBoxComponent, DifficultyLevelComponent, ArtemisSharedCommonModule], styleUrls: ['./exercise-headers-information.component.scss'], /* Our tsconfig file has `preserveWhitespaces: 'true'` which causes whitespace to affect content projection. @@ -31,6 +30,9 @@ import { ArtemisServerDateService } from 'app/shared/server-date.service'; preserveWhitespaces: false, }) export class ExerciseHeadersInformationComponent implements OnInit, OnChanges { + private sortService = inject(SortService); + private serverDateService = inject(ArtemisServerDateService); + readonly IncludedInOverallScore = IncludedInOverallScore; readonly dayjs = dayjs; @@ -47,11 +49,6 @@ export class ExerciseHeadersInformationComponent implements OnInit, OnChanges { numberOfSubmissions: number; informationBoxItems: InformationBox[] = []; - constructor( - private sortService: SortService, - private serverDateService: ArtemisServerDateService, - ) {} - ngOnInit() { this.dueDate = getExerciseDueDate(this.exercise, this.studentParticipation); this.now = this.serverDateService.now(); diff --git a/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers.module.ts b/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers.module.ts index bc3d6fe70024..bdbb3f2b0707 100644 --- a/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers.module.ts +++ b/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers.module.ts @@ -7,8 +7,14 @@ import { SubmissionResultStatusModule } from 'app/overview/submission-result-sta import { ExerciseCategoriesModule } from 'app/shared/exercise-categories/exercise-categories.module'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, SubmissionResultStatusModule, ExerciseCategoriesModule], - declarations: [HeaderExercisePageWithDetailsComponent, HeaderParticipationPageComponent], + imports: [ + ArtemisSharedModule, + ArtemisSharedComponentModule, + SubmissionResultStatusModule, + ExerciseCategoriesModule, + HeaderExercisePageWithDetailsComponent, + HeaderParticipationPageComponent, + ], exports: [HeaderExercisePageWithDetailsComponent, HeaderParticipationPageComponent], }) export class ArtemisHeaderExercisePageWithDetailsModule {} diff --git a/src/main/webapp/app/exercises/shared/exercise-headers/header-exercise-page-with-details.component.ts b/src/main/webapp/app/exercises/shared/exercise-headers/header-exercise-page-with-details.component.ts index b89694846761..dc7a09231379 100644 --- a/src/main/webapp/app/exercises/shared/exercise-headers/header-exercise-page-with-details.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-headers/header-exercise-page-with-details.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnInit, inject } from '@angular/core'; import { SortService } from 'app/shared/service/sort.service'; import dayjs from 'dayjs/esm'; import { Exercise, ExerciseType, IncludedInOverallScore, getCourseFromExercise, getIcon, getIconTooltip } from 'app/entities/exercise.model'; @@ -16,13 +16,35 @@ import { ComplaintService } from 'app/complaints/complaint.service'; import { SubmissionType } from 'app/entities/submission.model'; import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ExerciseCategoriesComponent } from 'app/shared/exercise-categories/exercise-categories.component'; +import { NgClass } from '@angular/common'; +import { IncludedInScoreBadgeComponent } from './included-in-score-badge.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; @Component({ selector: 'jhi-header-exercise-page-with-details', templateUrl: './header-exercise-page-with-details.component.html', styleUrls: ['./header-exercise-page-with-details.component.scss'], + imports: [ + FaIconComponent, + NgbTooltip, + ExerciseCategoriesComponent, + NgClass, + IncludedInScoreBadgeComponent, + TranslateDirective, + ArtemisDatePipe, + ArtemisTranslatePipe, + ArtemisTimeAgoPipe, + ], }) export class HeaderExercisePageWithDetailsComponent implements OnChanges, OnInit { + private sortService = inject(SortService); + readonly IncludedInOverallScore = IncludedInOverallScore; readonly AssessmentType = AssessmentType; readonly ExerciseType = ExerciseType; @@ -56,8 +78,6 @@ export class HeaderExercisePageWithDetailsComponent implements OnChanges, OnInit // Icons faQuestionCircle = faQuestionCircle; - constructor(private sortService: SortService) {} - ngOnInit() { this.exerciseCategories = this.exercise.categories || []; diff --git a/src/main/webapp/app/exercises/shared/exercise-headers/header-participation-page.component.ts b/src/main/webapp/app/exercises/shared/exercise-headers/header-participation-page.component.ts index 975808bde2ce..3a181e33f0f4 100644 --- a/src/main/webapp/app/exercises/shared/exercise-headers/header-participation-page.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-headers/header-participation-page.component.ts @@ -6,12 +6,31 @@ import { ButtonType } from 'app/shared/components/button.component'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; import { getExerciseDueDate, hasExerciseDueDatePassed } from 'app/exercises/shared/exercise/exercise.utils'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass, NgStyle } from '@angular/common'; +import { DifficultyBadgeComponent } from './difficulty-badge.component'; +import { IncludedInScoreBadgeComponent } from './included-in-score-badge.component'; +import { SubmissionResultStatusComponent } from 'app/overview/submission-result-status.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; @Component({ selector: 'jhi-header-participation-page', templateUrl: './header-participation-page.component.html', styleUrls: ['./header-participation-page.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + TranslateDirective, + NgClass, + NgStyle, + DifficultyBadgeComponent, + IncludedInScoreBadgeComponent, + SubmissionResultStatusComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + ArtemisTimeAgoPipe, + ], }) export class HeaderParticipationPageComponent implements OnInit, OnChanges { readonly ButtonType = ButtonType; @@ -51,7 +70,7 @@ export class HeaderParticipationPageComponent implements OnInit, OnChanges { /** * Sets the status badge and categories of the exercise on changes */ - ngOnChanges(): void { + ngOnChanges() { if (this.exercise) { this.exerciseStatusBadge = hasExerciseDueDatePassed(this.exercise, this.participation) ? 'bg-danger' : 'bg-success'; this.exerciseCategories = this.exercise.categories || []; diff --git a/src/main/webapp/app/exercises/shared/exercise-headers/included-in-score-badge.component.ts b/src/main/webapp/app/exercises/shared/exercise-headers/included-in-score-badge.component.ts index f512f1dda52f..55e3b4ff954d 100644 --- a/src/main/webapp/app/exercises/shared/exercise-headers/included-in-score-badge.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-headers/included-in-score-badge.component.ts @@ -1,22 +1,25 @@ -import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnDestroy, OnInit, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { IncludedInOverallScore } from 'app/entities/exercise.model'; import { Subscription } from 'rxjs'; +import { NgClass } from '@angular/common'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'jhi-included-in-score-badge', templateUrl: './included-in-score-badge.component.html', styleUrls: ['./included-in-score-badge.component.scss'], + imports: [NgClass, NgbTooltip], }) export class IncludedInScoreBadgeComponent implements OnInit, OnDestroy, OnChanges { + private translateService = inject(TranslateService); + @Input() includedInOverallScore: IncludedInOverallScore | undefined; public translatedEnum = ''; public translatedTooltip = ''; public badgeClass: string; private translateSubscription: Subscription; - constructor(private translateService: TranslateService) {} - /** * Sets the badge attributes based on the included in score enum */ @@ -26,7 +29,7 @@ export class IncludedInScoreBadgeComponent implements OnInit, OnDestroy, OnChang }); } - ngOnChanges(): void { + ngOnChanges() { this.setBadgeAttributes(); } diff --git a/src/main/webapp/app/exercises/shared/exercise-info/exercise-info.component.ts b/src/main/webapp/app/exercises/shared/exercise-info/exercise-info.component.ts index 6a8ae197055f..ae91ae8ac766 100644 --- a/src/main/webapp/app/exercises/shared/exercise-info/exercise-info.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-info/exercise-info.component.ts @@ -5,11 +5,16 @@ import { getExerciseDueDate } from 'app/exercises/shared/exercise/exercise.utils import dayjs from 'dayjs/esm'; import { ComplaintService } from 'app/complaints/complaint.service'; import { AssessmentType } from 'app/entities/assessment-type.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgTemplateOutlet } from '@angular/common'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exercise-info', templateUrl: './exercise-info.component.html', styleUrls: ['../../../shared/side-panel/side-panel.scss'], + imports: [TranslateDirective, NgTemplateOutlet, ArtemisDatePipe, ArtemisTranslatePipe], }) export class ExerciseInfoComponent implements OnInit { @Input() exercise: Exercise; diff --git a/src/main/webapp/app/exercises/shared/exercise-info/exercise-info.module.ts b/src/main/webapp/app/exercises/shared/exercise-info/exercise-info.module.ts index 7df67b084660..68c22aa94c80 100644 --- a/src/main/webapp/app/exercises/shared/exercise-info/exercise-info.module.ts +++ b/src/main/webapp/app/exercises/shared/exercise-info/exercise-info.module.ts @@ -3,8 +3,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ExerciseInfoComponent } from 'app/exercises/shared/exercise-info/exercise-info.component'; @NgModule({ - declarations: [ExerciseInfoComponent], exports: [ExerciseInfoComponent], - imports: [ArtemisSharedModule], + imports: [ArtemisSharedModule, ExerciseInfoComponent], }) export class ArtemisExerciseInfoModule {} diff --git a/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores-export-button.component.ts b/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores-export-button.component.ts index 704737e2b4d0..6a9ecc90ccbe 100644 --- a/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores-export-button.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores-export-button.component.ts @@ -3,7 +3,7 @@ import { roundValueSpecifiedByCourseSettings, scrollToTopOfPage } from 'app/shar import { AlertService } from 'app/core/util/alert.service'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { Exercise, ExerciseType, getCourseFromExercise } from 'app/entities/exercise.model'; -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { ResultService } from 'app/exercises/shared/result/result.service'; import { getTestCaseNamesFromResults, getTestCaseResults } from 'app/exercises/shared/result/result.utils'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; @@ -12,12 +12,20 @@ import { ResultWithPointsPerGradingCriterion } from 'app/entities/result-with-po import { faDownload } from '@fortawesome/free-solid-svg-icons'; import { download, generateCsv, mkConfig } from 'export-to-csv'; import { TestCaseResult } from 'app/entities/programming/test-case-result.model'; +import { NgbDropdown, NgbDropdownButtonItem, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exercise-scores-export-button', templateUrl: './exercise-scores-export-button.component.html', + imports: [NgbDropdown, NgbDropdownToggle, FaIconComponent, TranslateDirective, NgbDropdownMenu, NgbDropdownButtonItem, NgbDropdownItem, NgbTooltip, ArtemisTranslatePipe], }) export class ExerciseScoresExportButtonComponent implements OnInit { + private resultService = inject(ResultService); + private alertService = inject(AlertService); + @Input() exercises: Exercise[] = []; // Used to export multiple scores together @Input() exercise: Exercise | ProgrammingExercise; @@ -26,11 +34,6 @@ export class ExerciseScoresExportButtonComponent implements OnInit { // Icons faDownload = faDownload; - constructor( - private resultService: ResultService, - private alertService: AlertService, - ) {} - ngOnInit(): void { this.isProgrammingExerciseResults = this.exercises.concat(this.exercise).every((exercise) => exercise?.type === ExerciseType.PROGRAMMING); } diff --git a/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.component.ts b/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.component.ts index 35b86f595fca..e51f1d0d5afd 100644 --- a/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.component.ts @@ -1,9 +1,9 @@ -import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; import { Participation } from 'app/entities/participation/participation.model'; import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; import { Subscription, forkJoin } from 'rxjs'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { Course } from 'app/entities/course.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; @@ -24,9 +24,27 @@ import { faFileCode } from '@fortawesome/free-regular-svg-icons'; import { Range } from 'app/shared/util/utils'; import dayjs from 'dayjs/esm'; import { ExerciseCacheService } from 'app/exercises/shared/exercise/exercise-cache.service'; -import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'; +import { NgbDropdown, NgbDropdownMenu, NgbDropdownToggle, NgbPopover, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { PROFILE_LOCALVC } from 'app/app.constants'; import { isManualResult } from 'app/exercises/shared/result/result.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormsModule } from '@angular/forms'; +import { ExternalSubmissionButtonComponent } from '../external-submission/external-submission-button.component'; +import { ExerciseActionButtonComponent } from 'app/shared/components/exercise-action-button.component'; +import { ExerciseScoresExportButtonComponent } from './exercise-scores-export-button.component'; +import { ProgrammingAssessmentRepoExportButtonComponent } from '../../programming/assess/repo-export/programming-assessment-repo-export-button.component'; +import { SubmissionExportButtonComponent } from '../submission-export/submission-export-button.component'; +import { DataTableComponent } from 'app/shared/data-table/data-table.component'; +import { NgxDatatableModule } from '@siemens/ngx-datatable'; +import { ResultComponent } from '../result/result.component'; +import { CodeButtonComponent } from 'app/shared/components/code-button/code-button.component'; +import { FeatureToggleLinkDirective } from 'app/shared/feature-toggle/feature-toggle-link.directive'; +import { ManageAssessmentButtonsComponent } from './manage-assessment-buttons.component'; +import { DecimalPipe, KeyValuePipe } from '@angular/common'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; /** * Filter properties for a result @@ -46,8 +64,43 @@ export enum FilterProp { templateUrl: './exercise-scores.component.html', providers: [ExerciseCacheService], encapsulation: ViewEncapsulation.None, + imports: [ + TranslateDirective, + NgbDropdown, + NgbDropdownToggle, + FaIconComponent, + NgbDropdownMenu, + FormsModule, + RouterLink, + ExternalSubmissionButtonComponent, + ExerciseActionButtonComponent, + NgbPopover, + ExerciseScoresExportButtonComponent, + ProgrammingAssessmentRepoExportButtonComponent, + SubmissionExportButtonComponent, + DataTableComponent, + NgxDatatableModule, + ResultComponent, + NgbTooltip, + CodeButtonComponent, + FeatureToggleLinkDirective, + ManageAssessmentButtonsComponent, + DecimalPipe, + KeyValuePipe, + ArtemisDatePipe, + ArtemisTranslatePipe, + ArtemisDurationFromSecondsPipe, + ], }) export class ExerciseScoresComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private courseService = inject(CourseManagementService); + private exerciseService = inject(ExerciseService); + private resultService = inject(ResultService); + private profileService = inject(ProfileService); + private programmingSubmissionService = inject(ProgrammingSubmissionService); + private participationService = inject(ParticipationService); + // make constants available to html for comparison readonly FilterProp = FilterProp; readonly ExerciseType = ExerciseType; @@ -99,16 +152,6 @@ export class ExerciseScoresComponent implements OnInit, OnDestroy { faFilter = faFilter; faComment = faComment; - constructor( - private route: ActivatedRoute, - private courseService: CourseManagementService, - private exerciseService: ExerciseService, - private resultService: ResultService, - private profileService: ProfileService, - private programmingSubmissionService: ProgrammingSubmissionService, - private participationService: ParticipationService, - ) {} - /** * Fetches the course and exercise from the server */ diff --git a/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.module.ts b/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.module.ts index dbfa425c801d..3175c9664bb6 100644 --- a/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.module.ts +++ b/src/main/webapp/app/exercises/shared/exercise-scores/exercise-scores.module.ts @@ -6,7 +6,7 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { ArtemisProgrammingAssessmentModule } from 'app/exercises/programming/assess/programming-assessment.module'; import { NgxDatatableModule } from '@siemens/ngx-datatable'; import { ArtemisDataTableModule } from 'app/shared/data-table/data-table.module'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { ArtemisResultModule } from 'app/exercises/shared/result/result.module'; import { FormDateTimePickerModule } from 'app/shared/date-time-picker/date-time-picker.module'; import { ArtemisExerciseScoresRoutingModule } from 'app/exercises/shared/exercise-scores/exercise-scores-routing.module'; @@ -29,12 +29,9 @@ import { ExternalSubmissionButtonComponent } from 'app/exercises/shared/external NgxDatatableModule, ArtemisDataTableModule, ArtemisProgrammingAssessmentModule, - FeatureToggleModule, ArtemisSharedComponentModule, SubmissionResultStatusModule, ArtemisMarkdownModule, - ], - declarations: [ ExerciseScoresComponent, SubmissionExportButtonComponent, SubmissionExportDialogComponent, diff --git a/src/main/webapp/app/exercises/shared/exercise-scores/manage-assessment-buttons.component.ts b/src/main/webapp/app/exercises/shared/exercise-scores/manage-assessment-buttons.component.ts index 0a68b60efcab..abda4a9fad15 100644 --- a/src/main/webapp/app/exercises/shared/exercise-scores/manage-assessment-buttons.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-scores/manage-assessment-buttons.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { faBan, faFolderOpen } from '@fortawesome/free-solid-svg-icons'; import { TranslateService } from '@ngx-translate/core'; import { AssessmentType } from 'app/entities/assessment-type.model'; @@ -13,12 +13,21 @@ import { ProgrammingAssessmentManualResultService } from 'app/exercises/programm import { areManualResultsAllowed } from 'app/exercises/shared/exercise/exercise.utils'; import { TextAssessmentService } from 'app/exercises/text/assess/text-assessment.service'; import { getLinkToSubmissionAssessment } from 'app/utils/navigation.utils'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-manage-assessment-buttons', templateUrl: './manage-assessment-buttons.component.html', + imports: [RouterLink, FaIconComponent, ArtemisTranslatePipe], }) export class ManageAssessmentButtonsComponent implements OnInit { + private programmingAssessmentManualResultService = inject(ProgrammingAssessmentManualResultService); + private modelingAssessmentService = inject(ModelingAssessmentService); + private textAssessmentService = inject(TextAssessmentService); + private fileUploadAssessmentService = inject(FileUploadAssessmentService); + @Input() exercise: Exercise; @Input() course: Course; @Input() participation: Participation; @@ -28,20 +37,16 @@ export class ManageAssessmentButtonsComponent implements OnInit { correctionRoundIndices: number[]; cancelConfirmationText: string; - newManualResultAllowed: boolean = false; + newManualResultAllowed = false; examMode = false; readonly faBan = faBan; readonly faFolderOpen = faFolderOpen; readonly AssessmentType = AssessmentType; - constructor( - translateService: TranslateService, - private programmingAssessmentManualResultService: ProgrammingAssessmentManualResultService, - private modelingAssessmentService: ModelingAssessmentService, - private textAssessmentService: TextAssessmentService, - private fileUploadAssessmentService: FileUploadAssessmentService, - ) { + constructor() { + const translateService = inject(TranslateService); + translateService.get('artemisApp.programmingAssessment.confirmCancel').subscribe((text) => (this.cancelConfirmationText = text)); } diff --git a/src/main/webapp/app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component.ts b/src/main/webapp/app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component.ts index 5b53397e616e..2b03093487df 100644 --- a/src/main/webapp/app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component.ts @@ -8,40 +8,40 @@ import { CourseExistingExerciseDetailsType, ExerciseService } from 'app/exercise @Component({ selector: 'jhi-exercise-title-channel-name', templateUrl: './exercise-title-channel-name.component.html', + imports: [TitleChannelNameComponent], }) export class ExerciseTitleChannelNameComponent implements OnChanges { - @Input() exercise: Exercise; course = input(); + isEditFieldDisplayedRecord = input>(); + courseId = input(); + + @Input() exercise: Exercise; @Input() titlePattern: string; @Input() minTitleLength: number; @Input() isExamMode: boolean; @Input() isImport: boolean; @Input() hideTitleLabel: boolean; - isEditFieldDisplayedRecord = input>(); - courseId = input(); @ViewChild(TitleChannelNameComponent) titleChannelNameComponent: TitleChannelNameComponent; onTitleChange = output(); onChannelNameChange = output(); - private readonly exerciseService: ExerciseService = inject(ExerciseService); - alreadyUsedExerciseNames = signal>(new Set()); hideChannelNameInput = false; constructor() { + const exerciseService = inject(ExerciseService); effect( function fetchExistingExerciseNamesOnInit() { const courseId = this.courseId() ?? this.course()?.id; if (courseId && this.exercise.type) { - this.exerciseService.getExistingExerciseDetailsInCourse(courseId, this.exercise.type).subscribe((exerciseDetails: CourseExistingExerciseDetailsType) => { + exerciseService.getExistingExerciseDetailsInCourse(courseId, this.exercise.type).subscribe((exerciseDetails: CourseExistingExerciseDetailsType) => { this.alreadyUsedExerciseNames.set(exerciseDetails.exerciseTitles ?? new Set()); }); } }.bind(this), - { allowSignalWrites: true }, ); } diff --git a/src/main/webapp/app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.module.ts b/src/main/webapp/app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.module.ts index 908bbf1b51ed..625d0fd3b433 100644 --- a/src/main/webapp/app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.module.ts +++ b/src/main/webapp/app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.module.ts @@ -3,8 +3,7 @@ import { ExerciseTitleChannelNameComponent } from './exercise-title-channel-name import { TitleChannelNameModule } from 'app/shared/form/title-channel-name/title-channel-name.module'; @NgModule({ - declarations: [ExerciseTitleChannelNameComponent], - imports: [TitleChannelNameModule], + imports: [TitleChannelNameModule, ExerciseTitleChannelNameComponent], exports: [ExerciseTitleChannelNameComponent], }) export class ExerciseTitleChannelNameModule {} diff --git a/src/main/webapp/app/exercises/shared/exercise-update-notification/exercise-update-notification.component.ts b/src/main/webapp/app/exercises/shared/exercise-update-notification/exercise-update-notification.component.ts index 27207b1e2b78..a3dcb9b25f1c 100644 --- a/src/main/webapp/app/exercises/shared/exercise-update-notification/exercise-update-notification.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-update-notification/exercise-update-notification.component.ts @@ -1,11 +1,14 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exercise-update-notification', templateUrl: './exercise-update-notification.component.html', + imports: [FormsModule, ArtemisTranslatePipe], }) export class ExerciseUpdateNotificationComponent { - @Input() isCreation: boolean = false; + @Input() isCreation = false; @Input() isImport: boolean; @Input() notificationText?: string; @Output() notificationTextChange: EventEmitter = new EventEmitter(); diff --git a/src/main/webapp/app/exercises/shared/exercise-update-notification/exercise-update-notification.module.ts b/src/main/webapp/app/exercises/shared/exercise-update-notification/exercise-update-notification.module.ts index 49b8092e4df7..9f49b0c92b7a 100644 --- a/src/main/webapp/app/exercises/shared/exercise-update-notification/exercise-update-notification.module.ts +++ b/src/main/webapp/app/exercises/shared/exercise-update-notification/exercise-update-notification.module.ts @@ -3,8 +3,7 @@ import { ExerciseUpdateNotificationComponent } from 'app/exercises/shared/exerci import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @NgModule({ - declarations: [ExerciseUpdateNotificationComponent], - imports: [ArtemisSharedCommonModule], + imports: [ArtemisSharedCommonModule, ExerciseUpdateNotificationComponent], exports: [ExerciseUpdateNotificationComponent], }) export class ExerciseUpdateNotificationModule {} diff --git a/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.component.ts b/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.component.ts index 35c1fd7b004d..52114e0888af 100644 --- a/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.component.ts @@ -1,13 +1,19 @@ -import { Component, EventEmitter, Output } from '@angular/core'; +import { Component, EventEmitter, Output, inject } from '@angular/core'; import { faBan, faCheck, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-exercise-update-warning', templateUrl: './exercise-update-warning.component.html', styleUrls: ['./exercise-update-warning.component.scss'], + imports: [TranslateDirective, FormsModule, FaIconComponent], }) export class ExerciseUpdateWarningComponent { + activeModal = inject(NgbActiveModal); + instructionDeleted = false; creditChanged = false; deleteFeedback = false; @@ -27,8 +33,6 @@ export class ExerciseUpdateWarningComponent { faCheck = faCheck; faExclamationTriangle = faExclamationTriangle; - constructor(public activeModal: NgbActiveModal) {} - /** * Closes the modal */ diff --git a/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.module.ts b/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.module.ts index e8c1d59cbf74..1f03b98fdd32 100644 --- a/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.module.ts +++ b/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.module.ts @@ -6,8 +6,7 @@ import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; import { ExerciseUpdateWarningComponent } from 'app/exercises/shared/exercise-update-warning/exercise-update-warning.component'; @NgModule({ - declarations: [ExerciseUpdateWarningComponent], exports: [ExerciseUpdateWarningComponent], - imports: [CommonModule, ArtemisSharedModule, ArtemisMarkdownModule, ArtemisMarkdownEditorModule], + imports: [CommonModule, ArtemisSharedModule, ArtemisMarkdownModule, ArtemisMarkdownEditorModule, ExerciseUpdateWarningComponent], }) export class ArtemisExerciseUpdateWarningModule {} diff --git a/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.service.ts b/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.service.ts index 3f1ea61f19cf..df91228928ad 100644 --- a/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.service.ts +++ b/src/main/webapp/app/exercises/shared/exercise-update-warning/exercise-update-warning.service.ts @@ -1,4 +1,4 @@ -import { Component, Injectable } from '@angular/core'; +import { Component, Injectable, inject } from '@angular/core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { Exercise } from 'app/entities/exercise.model'; import { GradingInstruction } from 'app/exercises/shared/structured-grading-criterion/grading-instruction.model'; @@ -7,6 +7,8 @@ import dayjs from 'dayjs/esm'; @Injectable({ providedIn: 'root' }) export class ExerciseUpdateWarningService { + private modalService = inject(NgbModal); + private ngbModalRef: NgbModalRef; instructionDeleted: boolean; @@ -17,8 +19,6 @@ export class ExerciseUpdateWarningService { isExamMode: boolean; - constructor(private modalService: NgbModal) {} - /** * Open the modal with the given content for the given exercise. * @param component the content that should be shown diff --git a/src/main/webapp/app/exercises/shared/exercise/exercise-cache.service.ts b/src/main/webapp/app/exercises/shared/exercise/exercise-cache.service.ts index 87129bc218e4..c222b82d524f 100644 --- a/src/main/webapp/app/exercises/shared/exercise/exercise-cache.service.ts +++ b/src/main/webapp/app/exercises/shared/exercise/exercise-cache.service.ts @@ -1,11 +1,11 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import dayjs from 'dayjs/esm'; import { Observable, of, tap } from 'rxjs'; @Injectable() export class ExerciseCacheService { - constructor(private exerciseService: ExerciseService) {} + private exerciseService = inject(ExerciseService); latestDueDateByExerciseId: Map = new Map(); diff --git a/src/main/webapp/app/exercises/shared/exercise/exercise.component.ts b/src/main/webapp/app/exercises/shared/exercise/exercise.component.ts index 32d96f26b6f7..603617d31eef 100644 --- a/src/main/webapp/app/exercises/shared/exercise/exercise.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise/exercise.component.ts @@ -13,7 +13,9 @@ interface DeletionServiceInterface { delete: (id: number) => Observable>; } -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class ExerciseComponent implements OnInit, OnDestroy { protected translateService = inject(TranslateService); protected eventManager = inject(EventManager); @@ -29,7 +31,7 @@ export abstract class ExerciseComponent implements OnInit, OnDestroy { showHeading: boolean; courseId: number; predicate: string = 'id'; - reverse: boolean = true; + reverse = true; selectedExercises: Exercise[] = []; allChecked = false; diff --git a/src/main/webapp/app/exercises/shared/exercise/exercise.module.ts b/src/main/webapp/app/exercises/shared/exercise/exercise.module.ts index d155d9dd9970..999f65b53362 100644 --- a/src/main/webapp/app/exercises/shared/exercise/exercise.module.ts +++ b/src/main/webapp/app/exercises/shared/exercise/exercise.module.ts @@ -9,8 +9,15 @@ import { ArtemisChartsModule } from 'app/shared/chart/artemis-charts.module'; import { QuizExerciseLifecycleButtonsComponent } from 'app/exercises/quiz/manage/quiz-exercise-lifecycle-buttons.component'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, FormsModule, ArtemisChartsModule], - declarations: [ExerciseDetailStatisticsComponent, ExerciseStatisticsComponent, QuizExerciseLifecycleButtonsComponent], + imports: [ + ArtemisSharedModule, + ArtemisSharedComponentModule, + FormsModule, + ArtemisChartsModule, + ExerciseDetailStatisticsComponent, + ExerciseStatisticsComponent, + QuizExerciseLifecycleButtonsComponent, + ], exports: [ExerciseDetailStatisticsComponent, ExerciseStatisticsComponent, QuizExerciseLifecycleButtonsComponent], }) export class ArtemisExerciseModule {} diff --git a/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts b/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts index b377e0c20572..2e2aac255860 100644 --- a/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts +++ b/src/main/webapp/app/exercises/shared/exercise/exercise.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import dayjs from 'dayjs/esm'; @@ -56,17 +56,15 @@ export interface ExerciseServicable { @Injectable({ providedIn: 'root' }) export class ExerciseService { + private http = inject(HttpClient); + private accountService = inject(AccountService); + private translateService = inject(TranslateService); + private entityTitleService = inject(EntityTitleService); + public resourceUrl = 'api/exercises'; public adminResourceUrl = 'api/admin/exercises'; public courseResourceUrl = 'api/courses'; - constructor( - private http: HttpClient, - private accountService: AccountService, - private translateService: TranslateService, - private entityTitleService: EntityTitleService, - ) {} - /** * Persist a new exercise * @param { Exercise } exercise - Exercise that will be persisted diff --git a/src/main/webapp/app/exercises/shared/external-submission/external-submission-button.component.ts b/src/main/webapp/app/exercises/shared/external-submission/external-submission-button.component.ts index 3600def8329c..4a0f209c1f10 100644 --- a/src/main/webapp/app/exercises/shared/external-submission/external-submission-button.component.ts +++ b/src/main/webapp/app/exercises/shared/external-submission/external-submission-button.component.ts @@ -1,9 +1,10 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { faPlus } from '@fortawesome/free-solid-svg-icons'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { Exercise } from 'app/entities/exercise.model'; import { ExternalSubmissionDialogComponent } from 'app/exercises/shared/external-submission/external-submission-dialog.component'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; +import { ButtonComponent } from 'app/shared/components/button.component'; @Component({ selector: 'jhi-external-submission', @@ -18,8 +19,11 @@ import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; /> } `, + imports: [ButtonComponent], }) export class ExternalSubmissionButtonComponent { + private modalService = inject(NgbModal); + ButtonType = ButtonType; ButtonSize = ButtonSize; @@ -28,8 +32,6 @@ export class ExternalSubmissionButtonComponent { // Icons faPlus = faPlus; - constructor(private modalService: NgbModal) {} - /** * Opens modal window for external exercise submission. * @param { MouseEvent } event diff --git a/src/main/webapp/app/exercises/shared/external-submission/external-submission-dialog.component.ts b/src/main/webapp/app/exercises/shared/external-submission/external-submission-dialog.component.ts index e87e3b3a70d2..69e127e9d8d0 100644 --- a/src/main/webapp/app/exercises/shared/external-submission/external-submission-dialog.component.ts +++ b/src/main/webapp/app/exercises/shared/external-submission/external-submission-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Result } from 'app/entities/result.model'; import { Feedback, FeedbackType } from 'app/entities/feedback.model'; @@ -12,12 +12,23 @@ import { SCORE_PATTERN } from 'app/app.constants'; import { User } from 'app/core/user/user.model'; import { EventManager } from 'app/core/util/event-manager.service'; import { faBan, faSave } from '@fortawesome/free-solid-svg-icons'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-external-submission-dialog', templateUrl: './external-submission-dialog.component.html', + imports: [FormsModule, TranslateDirective, NgClass, FaIconComponent, HtmlForMarkdownPipe], }) export class ExternalSubmissionDialogComponent implements OnInit { + private participationService = inject(ParticipationService); + private externalSubmissionService = inject(ExternalSubmissionService); + private activeModal = inject(NgbActiveModal); + private eventManager = inject(EventManager); + readonly SCORE_PATTERN = SCORE_PATTERN; @Input() exercise: Exercise; @@ -34,13 +45,6 @@ export class ExternalSubmissionDialogComponent implements OnInit { faSave = faSave; faBan = faBan; - constructor( - private participationService: ParticipationService, - private externalSubmissionService: ExternalSubmissionService, - private activeModal: NgbActiveModal, - private eventManager: EventManager, - ) {} - /** * Initialize Component by calling a helper that generates an initial manual result. */ diff --git a/src/main/webapp/app/exercises/shared/external-submission/external-submission.service.ts b/src/main/webapp/app/exercises/shared/external-submission/external-submission.service.ts index ce954e34eb01..f7f90a39b503 100644 --- a/src/main/webapp/app/exercises/shared/external-submission/external-submission.service.ts +++ b/src/main/webapp/app/exercises/shared/external-submission/external-submission.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import dayjs from 'dayjs/esm'; @@ -10,11 +10,10 @@ import { map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class ExternalSubmissionService { + private http = inject(HttpClient); + private resultService = inject(ResultService); + // TODO: It would be good to refactor the convertDate methods into a separate service, so that we don't have to import the result service here. - constructor( - private http: HttpClient, - private resultService: ResultService, - ) {} /** * Persist a new result for the provided exercise and student (a participation and an empty submission will also be created if they do not exist yet) diff --git a/src/main/webapp/app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.component.ts b/src/main/webapp/app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.component.ts index 993b4154c449..56527ebfd121 100644 --- a/src/main/webapp/app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.component.ts +++ b/src/main/webapp/app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.component.ts @@ -1,19 +1,27 @@ -import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; +import { Component, Input, OnChanges, OnInit, SimpleChanges, inject } from '@angular/core'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { Observable } from 'rxjs'; import { AthenaService } from 'app/assessment/athena.service'; import { ActivatedRoute } from '@angular/router'; import dayjs from 'dayjs/esm'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { AsyncPipe, NgStyle } from '@angular/common'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { FormsModule } from '@angular/forms'; @Component({ selector: 'jhi-exercise-feedback-suggestion-options', templateUrl: './exercise-feedback-suggestion-options.component.html', + imports: [TranslateDirective, NgStyle, HelpIconComponent, FormsModule, AsyncPipe], }) export class ExerciseFeedbackSuggestionOptionsComponent implements OnInit, OnChanges { + private athenaService = inject(AthenaService); + private activatedRoute = inject(ActivatedRoute); + @Input() exercise: Exercise; @Input() dueDate?: dayjs.Dayjs; - @Input() readOnly: boolean = false; + @Input() readOnly = false; protected readonly ExerciseType = ExerciseType; @@ -26,11 +34,6 @@ export class ExerciseFeedbackSuggestionOptionsComponent implements OnInit, OnCha availableAthenaModules: string[]; initialAthenaModule?: string; - constructor( - private athenaService: AthenaService, - private activatedRoute: ActivatedRoute, - ) {} - ngOnInit(): void { const courseId = Number(this.activatedRoute.snapshot.paramMap.get('courseId')); this.athenaService.getAvailableModules(courseId, this.exercise).subscribe((modules) => { diff --git a/src/main/webapp/app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.module.ts b/src/main/webapp/app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.module.ts index 4e2acac9776b..ca2583caf7be 100644 --- a/src/main/webapp/app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.module.ts +++ b/src/main/webapp/app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.module.ts @@ -4,8 +4,7 @@ import { ExerciseFeedbackSuggestionOptionsComponent } from 'app/exercises/shared import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; @NgModule({ - declarations: [ExerciseFeedbackSuggestionOptionsComponent], - imports: [ArtemisSharedCommonModule, ArtemisSharedComponentModule], + imports: [ArtemisSharedCommonModule, ArtemisSharedComponentModule, ExerciseFeedbackSuggestionOptionsComponent], exports: [ExerciseFeedbackSuggestionOptionsComponent], }) export class ExerciseFeedbackSuggestionOptionsModule {} diff --git a/src/main/webapp/app/exercises/shared/feedback/collapse/feedback-collapse.component.ts b/src/main/webapp/app/exercises/shared/feedback/collapse/feedback-collapse.component.ts index 277266f4662e..4b4025038682 100644 --- a/src/main/webapp/app/exercises/shared/feedback/collapse/feedback-collapse.component.ts +++ b/src/main/webapp/app/exercises/shared/feedback/collapse/feedback-collapse.component.ts @@ -1,11 +1,15 @@ import { Component, Input, OnInit } from '@angular/core'; import { faAngleDown, faAngleRight } from '@fortawesome/free-solid-svg-icons'; import { FeedbackItem } from 'app/exercises/shared/feedback/item/feedback-item'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FeedbackTextComponent } from '../text/feedback-text.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-feedback-collapse', styleUrls: ['./feedback-collapse.scss'], templateUrl: './feedback-collapse.component.html', + imports: [FaIconComponent, FeedbackTextComponent, ArtemisTranslatePipe], }) /** * smallCharacterLimit can be adjusted make smaller or bigger items collapsable diff --git a/src/main/webapp/app/exercises/shared/feedback/feedback-suggestion-badge/feedback-suggestion-badge.component.ts b/src/main/webapp/app/exercises/shared/feedback/feedback-suggestion-badge/feedback-suggestion-badge.component.ts index 382cf3a045db..018aefafbbde 100644 --- a/src/main/webapp/app/exercises/shared/feedback/feedback-suggestion-badge/feedback-suggestion-badge.component.ts +++ b/src/main/webapp/app/exercises/shared/feedback/feedback-suggestion-badge/feedback-suggestion-badge.component.ts @@ -1,14 +1,20 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { faLightbulb } from '@fortawesome/free-solid-svg-icons'; import { TranslateService } from '@ngx-translate/core'; import { Feedback, FeedbackSuggestionType } from 'app/entities/feedback.model'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-feedback-suggestion-badge', templateUrl: './feedback-suggestion-badge.component.html', styleUrls: ['./feedback-suggestion-badge.component.scss'], + imports: [NgbTooltip, FaIconComponent, TranslateDirective], }) export class FeedbackSuggestionBadgeComponent { + private translateService = inject(TranslateService); + @Input() feedback: Feedback; @@ -18,8 +24,6 @@ export class FeedbackSuggestionBadgeComponent { // Icons faLightbulb = faLightbulb; - constructor(private translateService: TranslateService) {} - get text(): string { const feedbackSuggestionType = Feedback.getFeedbackSuggestionType(this.feedback); if (feedbackSuggestionType === FeedbackSuggestionType.ADAPTED) { diff --git a/src/main/webapp/app/exercises/shared/feedback/feedback-suggestions-pending-confirmation-dialog/feedback-suggestions-pending-confirmation-dialog.component.ts b/src/main/webapp/app/exercises/shared/feedback/feedback-suggestions-pending-confirmation-dialog/feedback-suggestions-pending-confirmation-dialog.component.ts index b0c996f5b6ff..2352f1b64c15 100644 --- a/src/main/webapp/app/exercises/shared/feedback/feedback-suggestions-pending-confirmation-dialog/feedback-suggestions-pending-confirmation-dialog.component.ts +++ b/src/main/webapp/app/exercises/shared/feedback/feedback-suggestions-pending-confirmation-dialog/feedback-suggestions-pending-confirmation-dialog.component.ts @@ -1,18 +1,23 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { faBan, faTimes } from '@fortawesome/free-solid-svg-icons'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass } from '@angular/common'; @Component({ selector: 'jhi-feedback-suggestions-pending-confirmation-dialog', templateUrl: './feedback-suggestions-pending-confirmation-dialog.component.html', + imports: [FormsModule, TranslateDirective, FaIconComponent, NgClass], }) export class FeedbackSuggestionsPendingConfirmationDialogComponent { + private activeModal = inject(NgbActiveModal); + // Icons faBan = faBan; faTimes = faTimes; - constructor(private activeModal: NgbActiveModal) {} - /** * Close the confirmation dialog */ diff --git a/src/main/webapp/app/exercises/shared/feedback/feedback.component.ts b/src/main/webapp/app/exercises/shared/feedback/feedback.component.ts index 4386aa37f323..9587fc20d684 100644 --- a/src/main/webapp/app/exercises/shared/feedback/feedback.component.ts +++ b/src/main/webapp/app/exercises/shared/feedback/feedback.component.ts @@ -1,6 +1,6 @@ -import { Component, Injector, Input, OnChanges, OnInit, Optional, SimpleChanges } from '@angular/core'; +import { Component, Injector, Input, OnChanges, OnInit, SimpleChanges, inject } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbActiveModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { catchError, map, switchMap, tap } from 'rxjs/operators'; import { of, throwError } from 'rxjs'; import { BuildLogEntry, BuildLogEntryArray, BuildLogType } from 'app/entities/programming/build-log.model'; @@ -16,7 +16,7 @@ import { createCommitUrl, isProgrammingExerciseParticipation } from 'app/exercis import { AssessmentType } from 'app/entities/assessment-type.model'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; -import { LegendPosition, ScaleType } from '@swimlane/ngx-charts'; +import { BarChartModule, LegendPosition, ScaleType } from '@swimlane/ngx-charts'; import { faCircleNotch, faExclamationTriangle, faXmark } from '@fortawesome/free-solid-svg-icons'; import { GraphColors } from 'app/entities/statistics.model'; import { axisTickFormattingWithPercentageSign } from 'app/shared/statistics-graph/statistics-graph.utils'; @@ -31,14 +31,43 @@ import { ChartData } from 'app/exercises/shared/feedback/chart/feedback-chart-da import { FeedbackChartService } from 'app/exercises/shared/feedback/chart/feedback-chart.service'; import { isFeedbackGroup } from 'app/exercises/shared/feedback/group/feedback-group'; import { cloneDeep } from 'lodash-es'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass, NgTemplateOutlet, UpperCasePipe } from '@angular/common'; +import { FeedbackNodeComponent } from './node/feedback-node.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; // Modal -> Result details view @Component({ selector: 'jhi-result-detail', templateUrl: './feedback.component.html', styleUrls: ['./feedback.scss'], + imports: [ + TranslateDirective, + FaIconComponent, + NgClass, + NgbTooltip, + BarChartModule, + NgTemplateOutlet, + FeedbackNodeComponent, + UpperCasePipe, + ArtemisDatePipe, + ArtemisTranslatePipe, + ArtemisTimeAgoPipe, + ], }) export class FeedbackComponent implements OnInit, OnChanges { + private resultService = inject(ResultService); + private buildLogService = inject(BuildLogService); + private translateService = inject(TranslateService); + private profileService = inject(ProfileService); + private feedbackService = inject(FeedbackService); + private feedbackChartService = inject(FeedbackChartService); + private injector = inject(Injector); + activeModal? = inject(NgbActiveModal, { optional: true }); + readonly BuildLogType = BuildLogType; readonly AssessmentType = AssessmentType; readonly ExerciseType = ExerciseType; @@ -72,8 +101,8 @@ export class FeedbackComponent implements OnInit, OnChanges { @Input() taskName?: string; @Input() numberOfNotExecutedTests?: number; - @Input() isExamReviewPage?: boolean = false; - @Input() isPrinting?: boolean = false; + @Input() isExamReviewPage = false; + @Input() isPrinting = false; // Icons faXmark = faXmark; @@ -114,17 +143,9 @@ export class FeedbackComponent implements OnInit, OnChanges { */ private feedbackItemNodesBeforePrinting: FeedbackNode[]; - constructor( - private resultService: ResultService, - private buildLogService: BuildLogService, - private translateService: TranslateService, - private profileService: ProfileService, - private feedbackService: FeedbackService, - private feedbackChartService: FeedbackChartService, - private injector: Injector, - @Optional() - public activeModal?: NgbActiveModal, - ) { + constructor() { + const translateService = this.translateService; + const pointsLabel = translateService.instant('artemisApp.result.chart.points'); const deductionsLabel = translateService.instant('artemisApp.result.chart.deductions'); this.labels = [pointsLabel, deductionsLabel]; diff --git a/src/main/webapp/app/exercises/shared/feedback/feedback.module.ts b/src/main/webapp/app/exercises/shared/feedback/feedback.module.ts index 82de5aedc16a..3bbcbd7a3ba2 100644 --- a/src/main/webapp/app/exercises/shared/feedback/feedback.module.ts +++ b/src/main/webapp/app/exercises/shared/feedback/feedback.module.ts @@ -12,8 +12,11 @@ import { FeedbackSuggestionBadgeComponent } from 'app/exercises/shared/feedback/ import { FeedbackSuggestionsPendingConfirmationDialogComponent } from 'app/exercises/shared/feedback/feedback-suggestions-pending-confirmation-dialog/feedback-suggestions-pending-confirmation-dialog.component'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisProgrammingExerciseActionsModule, ArtemisSharedComponentModule, BarChartModule], - declarations: [ + imports: [ + ArtemisSharedModule, + ArtemisProgrammingExerciseActionsModule, + ArtemisSharedComponentModule, + BarChartModule, FeedbackCollapseComponent, FeedbackNodeComponent, FeedbackComponent, diff --git a/src/main/webapp/app/exercises/shared/feedback/item/feedback-item-service.ts b/src/main/webapp/app/exercises/shared/feedback/item/feedback-item-service.ts index 13b0ca3d1aa4..2482195d8a22 100644 --- a/src/main/webapp/app/exercises/shared/feedback/item/feedback-item-service.ts +++ b/src/main/webapp/app/exercises/shared/feedback/item/feedback-item-service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Feedback } from 'app/entities/feedback.model'; import { TranslateService } from '@ngx-translate/core'; import { FeedbackItem } from 'app/exercises/shared/feedback/item/feedback-item'; @@ -25,7 +25,7 @@ export interface FeedbackItemService { @Injectable({ providedIn: 'root' }) export class FeedbackItemServiceImpl implements FeedbackItemService { - constructor(private translateService: TranslateService) {} + private translateService = inject(TranslateService); create(feedbacks: Feedback[], showTestDetails: boolean): FeedbackItem[] { return feedbacks.map((feedback) => this.createFeedbackItem(feedback, showTestDetails)); diff --git a/src/main/webapp/app/exercises/shared/feedback/item/programming-feedback-item.service.ts b/src/main/webapp/app/exercises/shared/feedback/item/programming-feedback-item.service.ts index 8b231443aa32..49eddf0f102f 100644 --- a/src/main/webapp/app/exercises/shared/feedback/item/programming-feedback-item.service.ts +++ b/src/main/webapp/app/exercises/shared/feedback/item/programming-feedback-item.service.ts @@ -1,5 +1,5 @@ import { FeedbackItemService } from 'app/exercises/shared/feedback/item/feedback-item-service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { FEEDBACK_SUGGESTION_ACCEPTED_IDENTIFIER, FEEDBACK_SUGGESTION_ADAPTED_IDENTIFIER, @@ -20,7 +20,7 @@ import { FeedbackGroup } from 'app/exercises/shared/feedback/group/feedback-grou @Injectable({ providedIn: 'root' }) export class ProgrammingFeedbackItemService implements FeedbackItemService { - constructor(private translateService: TranslateService) {} + private translateService = inject(TranslateService); create(feedbacks: Feedback[], showTestDetails: boolean): FeedbackItem[] { return feedbacks.map((feedback) => this.createFeedbackItem(feedback, showTestDetails)); diff --git a/src/main/webapp/app/exercises/shared/feedback/long-feedback-text.service.ts b/src/main/webapp/app/exercises/shared/feedback/long-feedback-text.service.ts index 60172ff40e5b..7c48aa6d24fc 100644 --- a/src/main/webapp/app/exercises/shared/feedback/long-feedback-text.service.ts +++ b/src/main/webapp/app/exercises/shared/feedback/long-feedback-text.service.ts @@ -1,4 +1,4 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable, Subject, Subscription, distinctUntilChanged, tap } from 'rxjs'; import { Cacheable } from 'ts-cacheable'; @@ -10,12 +10,10 @@ const logoutSubject = new Subject(); @Injectable({ providedIn: 'root' }) export class LongFeedbackTextService implements OnDestroy { - private userChangeSubscription: Subscription; + private http = inject(HttpClient); + private accountService = inject(AccountService); - constructor( - private http: HttpClient, - private accountService: AccountService, - ) {} + private userChangeSubscription: Subscription; init() { this.userChangeSubscription = this.accountService diff --git a/src/main/webapp/app/exercises/shared/feedback/node/feedback-node.component.ts b/src/main/webapp/app/exercises/shared/feedback/node/feedback-node.component.ts index 3de0f2fb389f..d2c1a4d6c68c 100644 --- a/src/main/webapp/app/exercises/shared/feedback/node/feedback-node.component.ts +++ b/src/main/webapp/app/exercises/shared/feedback/node/feedback-node.component.ts @@ -5,11 +5,18 @@ import { faAngleDown, faAngleUp, faExclamationTriangle } from '@fortawesome/free import { FeedbackGroup, isFeedbackGroup } from 'app/exercises/shared/feedback/group/feedback-group'; import { FeedbackItem } from 'app/exercises/shared/feedback/item/feedback-item'; import { FeedbackNode } from 'app/exercises/shared/feedback/node/feedback-node'; +import { NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FeedbackCollapseComponent } from '../collapse/feedback-collapse.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-feedback-node', templateUrl: './feedback-node.component.html', styleUrls: ['./feedback-node.scss'], + imports: [NgClass, FaIconComponent, NgbTooltip, FeedbackCollapseComponent, TranslateDirective, ArtemisTranslatePipe], }) export class FeedbackNodeComponent implements OnInit { readonly roundValueSpecifiedByCourseSettings = roundValueSpecifiedByCourseSettings; diff --git a/src/main/webapp/app/exercises/shared/feedback/standalone-feedback/standalone-feedback.component.ts b/src/main/webapp/app/exercises/shared/feedback/standalone-feedback/standalone-feedback.component.ts index 566bb17362cb..2cd9ff2870a4 100644 --- a/src/main/webapp/app/exercises/shared/feedback/standalone-feedback/standalone-feedback.component.ts +++ b/src/main/webapp/app/exercises/shared/feedback/standalone-feedback/standalone-feedback.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Optional } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { Result } from 'app/entities/result.model'; import dayjs from 'dayjs/esm'; @@ -7,13 +7,19 @@ import { ActivatedRoute } from '@angular/router'; import { HttpResponse } from '@angular/common/http'; import { ExerciseCacheService } from 'app/exercises/shared/exercise/exercise-cache.service'; import { ResultTemplateStatus, evaluateTemplateStatus } from 'app/exercises/shared/result/result.utils'; +import { FeedbackComponent } from '../feedback.component'; @Component({ selector: 'jhi-standalone-feedback', templateUrl: './standalone-feedback.component.html', styleUrls: ['./../feedback.scss', 'standalone-feedback.scss'], + imports: [FeedbackComponent], }) export class StandaloneFeedbackComponent implements OnInit { + route = inject(ActivatedRoute); + private exerciseService = inject(ExerciseService); + private exerciseCacheService = inject(ExerciseCacheService, { optional: true }); + exercise?: Exercise; result?: Result; @@ -23,12 +29,6 @@ export class StandaloneFeedbackComponent implements OnInit { latestDueDate?: dayjs.Dayjs; - constructor( - public route: ActivatedRoute, - private exerciseService: ExerciseService, - @Optional() private exerciseCacheService: ExerciseCacheService, - ) {} - ngOnInit(): void { this.route.params.subscribe((params) => { const exerciseId = parseInt(params['exerciseId'], 10); diff --git a/src/main/webapp/app/exercises/shared/feedback/text/feedback-text.component.ts b/src/main/webapp/app/exercises/shared/feedback/text/feedback-text.component.ts index cce1e3130dd5..120e0b81a74d 100644 --- a/src/main/webapp/app/exercises/shared/feedback/text/feedback-text.component.ts +++ b/src/main/webapp/app/exercises/shared/feedback/text/feedback-text.component.ts @@ -1,13 +1,17 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { FeedbackItem } from 'app/exercises/shared/feedback/item/feedback-item'; import { LongFeedbackTextService } from 'app/exercises/shared/feedback/long-feedback-text.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-feedback-text', styleUrls: ['./feedback-text.scss'], templateUrl: './feedback-text.component.html', + imports: [TranslateDirective], }) export class FeedbackTextComponent implements OnInit { + private longFeedbackService = inject(LongFeedbackTextService); + private readonly MAX_DISPLAYABLE_LENGTH = 20_000; @Input() feedback: FeedbackItem; @@ -17,8 +21,6 @@ export class FeedbackTextComponent implements OnInit { downloadText?: string; downloadFilename?: string; - constructor(private longFeedbackService: LongFeedbackTextService) {} - ngOnInit(): void { this.text = this.feedback.text ?? ''; diff --git a/src/main/webapp/app/exercises/shared/import/exercise-import-tabs.component.ts b/src/main/webapp/app/exercises/shared/import/exercise-import-tabs.component.ts index 518d66a11b0e..48045276c0ec 100644 --- a/src/main/webapp/app/exercises/shared/import/exercise-import-tabs.component.ts +++ b/src/main/webapp/app/exercises/shared/import/exercise-import-tabs.component.ts @@ -1,9 +1,25 @@ import { Component, Input } from '@angular/core'; import { ExerciseType } from 'app/entities/exercise.model'; +import { NgbNav, NgbNavContent, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavLinkBase, NgbNavOutlet } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ExerciseImportComponent } from './exercise-import.component'; +import { ExerciseImportFromFileComponent } from './from-file/exercise-import-from-file.component'; @Component({ selector: 'jhi-exercise-import-tabs', templateUrl: './exercise-import-tabs.component.html', + imports: [ + NgbNav, + NgbNavItem, + NgbNavItemRole, + NgbNavLink, + NgbNavLinkBase, + TranslateDirective, + NgbNavContent, + ExerciseImportComponent, + ExerciseImportFromFileComponent, + NgbNavOutlet, + ], }) export class ExerciseImportTabsComponent { activeTab = 1; diff --git a/src/main/webapp/app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component.ts b/src/main/webapp/app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component.ts index 23673a0cd2da..a5a9a69a06c6 100644 --- a/src/main/webapp/app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component.ts +++ b/src/main/webapp/app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component.ts @@ -1,13 +1,20 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ExerciseType } from 'app/entities/exercise.model'; import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ExerciseImportTabsComponent } from '../exercise-import-tabs.component'; +import { ExerciseImportComponent } from '../exercise-import.component'; @Component({ selector: 'jhi-exercise-import-wrapper', templateUrl: './exercise-import-wrapper.component.html', + imports: [FormsModule, TranslateDirective, ExerciseImportTabsComponent, ExerciseImportComponent], }) export class ExerciseImportWrapperComponent implements OnInit { + private activeModal = inject(NgbActiveModal); + readonly ExerciseType = ExerciseType; @Input() @@ -16,8 +23,6 @@ export class ExerciseImportWrapperComponent implements OnInit { @Input() programmingLanguage?: ProgrammingLanguage; - constructor(private activeModal: NgbActiveModal) {} - ngOnInit(): void { if (this.programmingLanguage) { this.titleKey = 'artemisApp.programmingExercise.configureGrading.categories.importLabel'; diff --git a/src/main/webapp/app/exercises/shared/import/exercise-import.component.ts b/src/main/webapp/app/exercises/shared/import/exercise-import.component.ts index 1fea53eaacbd..c4e8069d420d 100644 --- a/src/main/webapp/app/exercises/shared/import/exercise-import.component.ts +++ b/src/main/webapp/app/exercises/shared/import/exercise-import.component.ts @@ -1,6 +1,4 @@ -import { Component, Injector, Input, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { Component, Injector, Input, OnInit, inject } from '@angular/core'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { FileUploadExercisePagingService } from 'app/exercises/file-upload/manage/file-upload-exercise-paging.service'; @@ -11,15 +9,25 @@ import { QuizExercisePagingService } from 'app/exercises/quiz/manage/quiz-exerci import { ExercisePagingService } from 'app/exercises/shared/manage/exercise-paging.service'; import { TextExercisePagingService } from 'app/exercises/text/manage/text-exercise/text-exercise-paging.service'; import { ImportComponent } from 'app/shared/import/import.component'; -import { SortService } from 'app/shared/service/sort.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbHighlight, NgbPagination } from '@ng-bootstrap/ng-bootstrap'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { ExerciseCourseTitlePipe } from 'app/shared/pipes/exercise-course-title.pipe'; const DEFAULT_SORT_COLUMN = 'ID'; @Component({ selector: 'jhi-exercise-import', templateUrl: './exercise-import.component.html', + imports: [TranslateDirective, FormsModule, SortDirective, SortByDirective, FaIconComponent, NgbHighlight, ButtonComponent, NgbPagination, ExerciseCourseTitlePipe], }) export class ExerciseImportComponent extends ImportComponent implements OnInit { + private injector = inject(Injector); + readonly ExerciseType = ExerciseType; @Input() exerciseType?: ExerciseType; @@ -36,16 +44,11 @@ export class ExerciseImportComponent extends ImportComponent implement titleKey: string; - constructor( - router: Router, - sortService: SortService, - activeModal: NgbActiveModal, - private injector: Injector, - ) { + constructor() { // The exercise import component does not know yet which paging service to use // This gets determined based on the exercise type, which is not set when invoking the constructor - // Therefore we temporaily use this empty paging service which directly gets overwritten in ngOnInit(). - super(router, sortService, activeModal, undefined); + // Therefore we temporally use this empty paging service which directly gets overwritten in ngOnInit(). + super(undefined); } ngOnInit(): void { diff --git a/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts b/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts index 79083e180ea2..667533c2593b 100644 --- a/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts +++ b/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { ProgrammingExerciseBuildConfig } from 'app/entities/programming/programming-exercise-build.config'; @@ -7,12 +7,18 @@ import { AlertService } from 'app/core/util/alert.service'; import { faUpload } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExercise, copyBuildConfigFromExerciseJson } from 'app/entities/programming/programming-exercise.model'; import JSZip from 'jszip'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; @Component({ selector: 'jhi-exercise-import-from-file', templateUrl: './exercise-import-from-file.component.html', + imports: [ButtonComponent, HelpIconComponent], }) export class ExerciseImportFromFileComponent implements OnInit { + private activeModal = inject(NgbActiveModal); + private alertService = inject(AlertService); + @Input() exerciseType: ExerciseType; @Input() exercise: Exercise; @@ -21,11 +27,6 @@ export class ExerciseImportFromFileComponent implements OnInit { //Icons faUpload = faUpload; - constructor( - private activeModal: NgbActiveModal, - private alertService: AlertService, - ) {} - ngOnInit(): void { this.titleKey = this.exerciseType === ExerciseType.FILE_UPLOAD ? `artemisApp.fileUploadExercise.importFromFile.title` : `artemisApp.${this.exerciseType}Exercise.importFromFile.title`; diff --git a/src/main/webapp/app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.component.ts b/src/main/webapp/app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.component.ts index 6c18d0c28609..6c3f7f288e6e 100644 --- a/src/main/webapp/app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.component.ts +++ b/src/main/webapp/app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.component.ts @@ -1,10 +1,12 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { IncludedInOverallScore } from 'app/entities/exercise.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass } from '@angular/common'; @Component({ selector: 'jhi-included-in-overall-score-picker', templateUrl: './included-in-overall-score-picker.component.html', - styles: [], + imports: [TranslateDirective, NgClass], }) export class IncludedInOverallScorePickerComponent { readonly IncludedInOverallScore = IncludedInOverallScore; diff --git a/src/main/webapp/app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.module.ts b/src/main/webapp/app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.module.ts index b350ee97f1b4..700b2b017ef6 100644 --- a/src/main/webapp/app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.module.ts +++ b/src/main/webapp/app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.module.ts @@ -5,8 +5,7 @@ import { IncludedInOverallScorePickerComponent } from 'app/exercises/shared/incl import { FormsModule } from '@angular/forms'; @NgModule({ - imports: [ArtemisSharedModule, FormsModule], - declarations: [IncludedInOverallScorePickerComponent], + imports: [ArtemisSharedModule, FormsModule, IncludedInOverallScorePickerComponent], exports: [IncludedInOverallScorePickerComponent], }) export class ArtemisIncludedInOverallScorePickerModule {} diff --git a/src/main/webapp/app/exercises/shared/manage/exercise-create-buttons.component.ts b/src/main/webapp/app/exercises/shared/manage/exercise-create-buttons.component.ts index f24d1d507c9d..0edcc9e469b9 100644 --- a/src/main/webapp/app/exercises/shared/manage/exercise-create-buttons.component.ts +++ b/src/main/webapp/app/exercises/shared/manage/exercise-create-buttons.component.ts @@ -1,17 +1,23 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { Course } from 'app/entities/course.model'; import { faFileImport, faPlus } from '@fortawesome/free-solid-svg-icons'; import { ExerciseImportWrapperComponent } from 'app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component'; import { getIcon } from 'app/entities/exercise.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { Router } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-exercise-create-buttons', templateUrl: './exercise-create-buttons.component.html', + imports: [RouterLink, FaIconComponent, TranslateDirective], }) export class ExerciseCreateButtonsComponent implements OnInit { + private router = inject(Router); + private modalService = inject(NgbModal); + @Input() course: Course; @Input() exerciseType: ExerciseType; @@ -22,11 +28,6 @@ export class ExerciseCreateButtonsComponent implements OnInit { getExerciseTypeIcon = getIcon; - constructor( - private router: Router, - private modalService: NgbModal, - ) {} - ngOnInit(): void { if (this.exerciseType === ExerciseType.FILE_UPLOAD) { this.translationLabel = 'fileUpload'; diff --git a/src/main/webapp/app/exercises/shared/manage/exercise-create-buttons.module.ts b/src/main/webapp/app/exercises/shared/manage/exercise-create-buttons.module.ts index 819f3d0c1b2a..3b58d394f06f 100644 --- a/src/main/webapp/app/exercises/shared/manage/exercise-create-buttons.module.ts +++ b/src/main/webapp/app/exercises/shared/manage/exercise-create-buttons.module.ts @@ -4,8 +4,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisFileUploadExerciseManagementRoutingModule } from 'app/exercises/file-upload/manage/file-upload-exercise-management.route'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisFileUploadExerciseManagementRoutingModule], - declarations: [ExerciseCreateButtonsComponent], + imports: [ArtemisSharedModule, ArtemisFileUploadExerciseManagementRoutingModule, ExerciseCreateButtonsComponent], exports: [ExerciseCreateButtonsComponent], }) export class ArtemisExerciseCreateButtonsModule {} diff --git a/src/main/webapp/app/exercises/shared/mode-picker/mode-picker.component.ts b/src/main/webapp/app/exercises/shared/mode-picker/mode-picker.component.ts index 331246ab71e7..2b87f23e23cb 100644 --- a/src/main/webapp/app/exercises/shared/mode-picker/mode-picker.component.ts +++ b/src/main/webapp/app/exercises/shared/mode-picker/mode-picker.component.ts @@ -1,4 +1,6 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { NgClass } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export type ModePickerOption = { value: TMode; @@ -11,6 +13,7 @@ export type ModePickerOption = { selector: 'jhi-mode-picker', templateUrl: './mode-picker.component.html', styles: ['.btn.disabled { pointer-events: none }', '.btn-group.disabled { cursor: not-allowed; }'], + imports: [NgClass, ArtemisTranslatePipe], }) export class ModePickerComponent { @Input() options: ModePickerOption[]; diff --git a/src/main/webapp/app/exercises/shared/mode-picker/mode-picker.module.ts b/src/main/webapp/app/exercises/shared/mode-picker/mode-picker.module.ts index aeb12a24138c..7236b3af9902 100644 --- a/src/main/webapp/app/exercises/shared/mode-picker/mode-picker.module.ts +++ b/src/main/webapp/app/exercises/shared/mode-picker/mode-picker.module.ts @@ -4,8 +4,7 @@ import { ModePickerComponent } from 'app/exercises/shared/mode-picker/mode-picke import { ArtemisSharedModule } from 'app/shared/shared.module'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [ModePickerComponent], + imports: [ArtemisSharedModule, ModePickerComponent], exports: [ModePickerComponent], }) export class ArtemisModePickerModule {} diff --git a/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.component.ts b/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.component.ts index 733059f76d8d..a0d06d84df23 100644 --- a/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.component.ts +++ b/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { SubmissionService } from 'app/exercises/shared/submission/submission.service'; import { Subject, Subscription, combineLatest, of } from 'rxjs'; @@ -27,12 +27,35 @@ import { createCommitUrl } from 'app/exercises/programming/shared/utils/programm import { EventManager } from 'app/core/util/event-manager.service'; import { getExerciseDueDate, hasExerciseDueDatePassed } from 'app/exercises/shared/exercise/exercise.utils'; import { faTrash } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass } from '@angular/common'; +import { NgxDatatableModule } from '@siemens/ngx-datatable'; +import { ResultComponent } from '../result/result.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; @Component({ selector: 'jhi-participation-submission', templateUrl: './participation-submission.component.html', + imports: [TranslateDirective, NgClass, NgxDatatableModule, ResultComponent, DeleteButtonDirective, FaIconComponent, ArtemisDatePipe, ArtemisTranslatePipe, ArtemisTimeAgoPipe], }) export class ParticipationSubmissionComponent implements OnInit { + private route = inject(ActivatedRoute); + private submissionService = inject(SubmissionService); + private translateService = inject(TranslateService); + private participationService = inject(ParticipationService); + private exerciseService = inject(ExerciseService); + private programmingExerciseService = inject(ProgrammingExerciseService); + private fileUploadAssessmentService = inject(FileUploadAssessmentService); + private modelingAssessmentsService = inject(ModelingAssessmentService); + private textAssessmentService = inject(TextAssessmentService); + private programmingAssessmentService = inject(ProgrammingAssessmentManualResultService); + private eventManager = inject(EventManager); + private profileService = inject(ProfileService); + readonly ParticipationType = ParticipationType; readonly buttonSizeSmall = ButtonSize.SMALL; readonly actionTypeEmpty = ActionType.NoButtonTextDelete; @@ -58,21 +81,6 @@ export class ParticipationSubmissionComponent implements OnInit { // Icons faTrash = faTrash; - constructor( - private route: ActivatedRoute, - private submissionService: SubmissionService, - private translateService: TranslateService, - private participationService: ParticipationService, - private exerciseService: ExerciseService, - private programmingExerciseService: ProgrammingExerciseService, - private fileUploadAssessmentService: FileUploadAssessmentService, - private modelingAssessmentsService: ModelingAssessmentService, - private textAssessmentService: TextAssessmentService, - private programmingAssessmentService: ProgrammingAssessmentManualResultService, - private eventManager: EventManager, - private profileService: ProfileService, - ) {} - /** * Initialize component by setting up page and subscribe to eventManager */ diff --git a/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.module.ts b/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.module.ts index 1589e1ee6e89..ae5c0eb3b073 100644 --- a/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.module.ts +++ b/src/main/webapp/app/exercises/shared/participation-submission/participation-submission.module.ts @@ -7,7 +7,13 @@ import { ArtemisParticipationSubmissionRoutingModule } from 'app/exercises/share import { SubmissionResultStatusModule } from 'app/overview/submission-result-status.module'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisParticipationSubmissionRoutingModule, NgxDatatableModule, ArtemisResultModule, SubmissionResultStatusModule], - declarations: [ParticipationSubmissionComponent], + imports: [ + ArtemisSharedModule, + ArtemisParticipationSubmissionRoutingModule, + NgxDatatableModule, + ArtemisResultModule, + SubmissionResultStatusModule, + ParticipationSubmissionComponent, + ], }) export class ArtemisParticipationSubmissionModule {} diff --git a/src/main/webapp/app/exercises/shared/participation/participation.component.html b/src/main/webapp/app/exercises/shared/participation/participation.component.html index 27f175995eef..c4897def98da 100644 --- a/src/main/webapp/app/exercises/shared/participation/participation.component.html +++ b/src/main/webapp/app/exercises/shared/participation/participation.component.html @@ -22,7 +22,7 @@

    /> - @if (exercise.type === ExerciseType.PROGRAMMING && afterDueDate) { + @if (exercise?.type === ExerciseType.PROGRAMMING && afterDueDate) {

    diff --git a/src/main/webapp/app/orion/management/orion-programming-exercise.component.ts b/src/main/webapp/app/orion/management/orion-programming-exercise.component.ts index 2854122be21c..1d42aecb6e40 100644 --- a/src/main/webapp/app/orion/management/orion-programming-exercise.component.ts +++ b/src/main/webapp/app/orion/management/orion-programming-exercise.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { OrionConnectorService } from 'app/shared/orion/orion-connector.service'; import { ExerciseView, OrionState } from 'app/shared/orion/orion'; @@ -7,12 +7,20 @@ import { Course } from 'app/entities/course.model'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; import { ExerciseFilter } from 'app/entities/exercise-filter.model'; import { OrionButtonType } from 'app/shared/orion/orion-button/orion-button.component'; +import { ProgrammingExerciseComponent } from 'app/exercises/programming/manage/programming-exercise.component'; +import { OrionButtonComponent } from 'app/shared/orion/orion-button/orion-button.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-orion-programming-exercise', templateUrl: './orion-programming-exercise.component.html', + imports: [ProgrammingExerciseComponent, OrionButtonComponent, ArtemisTranslatePipe], }) export class OrionProgrammingExerciseComponent implements OnInit { + private orionConnectorService = inject(OrionConnectorService); + private router = inject(Router); + private programmingExerciseService = inject(ProgrammingExerciseService); + @Input() embedded = false; @Input() course: Course; @Input() exerciseFilter: ExerciseFilter; @@ -23,16 +31,12 @@ export class OrionProgrammingExerciseComponent implements OnInit { protected readonly OrionButtonType = OrionButtonType; orionState: OrionState; - constructor( - private orionConnectorService: OrionConnectorService, - private router: Router, - private programmingExerciseService: ProgrammingExerciseService, - ) {} - ngOnInit() { - this.orionConnectorService.state().subscribe((state) => { - this.orionState = state; - }); + if (this.orionConnectorService && this.orionConnectorService.state()) { + this.orionConnectorService.state().subscribe((state) => { + this.orionState = state; + }); + } } /** diff --git a/src/main/webapp/app/orion/participation/orion-course-exercise-details.component.ts b/src/main/webapp/app/orion/participation/orion-course-exercise-details.component.ts index a330a872b25d..d24f48cc8246 100644 --- a/src/main/webapp/app/orion/participation/orion-course-exercise-details.component.ts +++ b/src/main/webapp/app/orion/participation/orion-course-exercise-details.component.ts @@ -1,4 +1,6 @@ import { Component } from '@angular/core'; +import { CourseExerciseDetailsComponent } from 'app/overview/exercise-details/course-exercise-details.component'; +import { OrionExerciseDetailsStudentActionsComponent } from './orion-exercise-details-student-actions.component'; @Component({ selector: 'jhi-orion-course-exercise-details', @@ -9,6 +11,7 @@ import { Component } from '@angular/core'; `, + imports: [CourseExerciseDetailsComponent, OrionExerciseDetailsStudentActionsComponent], }) export class OrionCourseExerciseDetailsComponent { // only replaces the student actions with Orion student actions in the overview diff --git a/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.html b/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.html index e91dda6f3f21..f98986f2f7fc 100644 --- a/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.html +++ b/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.html @@ -1,34 +1,36 @@ - @if (isOfflineIdeAllowed && (orionState.view !== ExerciseView.STUDENT || orionState.opened !== this.exercise.id)) { - - } - @if (isOfflineIdeAllowed && orionState.view === ExerciseView.STUDENT && orionState.opened === this.exercise.id) { - - } - @if (isOfflineIdeAllowed && orionState.view === ExerciseView.STUDENT && orionState.opened === this.exercise.id && isDueDatePassed()) { - + @if (orionState !== undefined && orionState.view !== undefined) { + @if (isOfflineIdeAllowed && (orionState.view !== ExerciseView.STUDENT || orionState.opened !== this.exercise.id)) { + + } + @if (isOfflineIdeAllowed && orionState.view === ExerciseView.STUDENT && orionState.opened === this.exercise.id) { + + } + @if (isOfflineIdeAllowed && orionState.view === ExerciseView.STUDENT && orionState.opened === this.exercise.id && isDueDatePassed()) { + + } } diff --git a/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.ts b/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.ts index 30f6b585e32c..ebeba647bcfc 100644 --- a/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.ts +++ b/src/main/webapp/app/orion/participation/orion-exercise-details-student-actions.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; @@ -8,13 +8,21 @@ import { OrionConnectorService } from 'app/shared/orion/orion-connector.service' import { OrionBuildAndTestService } from 'app/shared/orion/orion-build-and-test.service'; import { Exercise } from 'app/entities/exercise.model'; import { OrionButtonType } from 'app/shared/orion/orion-button/orion-button.component'; +import { ExerciseDetailsStudentActionsComponent } from 'app/overview/exercise-details/exercise-details-student-actions.component'; +import { OrionButtonComponent } from 'app/shared/orion/orion-button/orion-button.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-orion-exercise-details-student-actions', templateUrl: './orion-exercise-details-student-actions.component.html', styleUrls: ['../../overview/course-overview.scss'], + imports: [ExerciseDetailsStudentActionsComponent, OrionButtonComponent, ArtemisTranslatePipe], }) export class OrionExerciseDetailsStudentActionsComponent implements OnInit { + private orionConnectorService = inject(OrionConnectorService); + private ideBuildAndTestService = inject(OrionBuildAndTestService); + private route = inject(ActivatedRoute); + readonly ExerciseView = ExerciseView; orionState: OrionState; FeatureToggle = FeatureToggle; @@ -23,24 +31,21 @@ export class OrionExerciseDetailsStudentActionsComponent implements OnInit { @Input() courseId: number; @Input() smallButtons: boolean; @Input() examMode: boolean; - protected readonly OrionButtonType = OrionButtonType; - constructor( - private orionConnectorService: OrionConnectorService, - private ideBuildAndTestService: OrionBuildAndTestService, - private route: ActivatedRoute, - ) {} + protected readonly OrionButtonType = OrionButtonType; /** * get orionState and submit changes if withIdeSubmit set in route query */ ngOnInit(): void { - this.orionConnectorService.state().subscribe((orionState: OrionState) => (this.orionState = orionState)); - this.route.queryParams.subscribe((params) => { - if (params['withIdeSubmit']) { - this.submitChanges(); - } - }); + if (this.orionConnectorService && this.orionConnectorService.state()) { + this.orionConnectorService.state().subscribe((orionState: OrionState) => (this.orionState = orionState)); + this.route.queryParams.subscribe((params) => { + if (params['withIdeSubmit']) { + this.submitChanges(); + } + }); + } } get isOfflineIdeAllowed() { diff --git a/src/main/webapp/app/overview/course-archive/course-archive.component.ts b/src/main/webapp/app/overview/course-archive/course-archive.component.ts index eb88ee898038..2bc638119b41 100644 --- a/src/main/webapp/app/overview/course-archive/course-archive.component.ts +++ b/src/main/webapp/app/overview/course-archive/course-archive.component.ts @@ -16,7 +16,6 @@ import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.co selector: 'jhi-course-archive', templateUrl: './course-archive.component.html', styleUrls: ['./course-archive.component.scss'], - standalone: true, imports: [ArtemisSharedModule, CourseCardHeaderComponent, SearchFilterComponent], }) export class CourseArchiveComponent implements OnInit, OnDestroy { diff --git a/src/main/webapp/app/overview/course-card-header/course-card-header.component.ts b/src/main/webapp/app/overview/course-card-header/course-card-header.component.ts index 3b1bb9b1c433..9aef1b0a8150 100644 --- a/src/main/webapp/app/overview/course-card-header/course-card-header.component.ts +++ b/src/main/webapp/app/overview/course-card-header/course-card-header.component.ts @@ -7,7 +7,6 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; selector: 'jhi-course-card-header', templateUrl: './course-card-header.component.html', styleUrls: ['./course-card-header.component.scss'], - standalone: true, imports: [ArtemisSharedModule], }) export class CourseCardHeaderComponent implements OnInit { diff --git a/src/main/webapp/app/overview/course-card.component.ts b/src/main/webapp/app/overview/course-card.component.ts index ce8bbb0bcc7e..7e9cde3079c8 100644 --- a/src/main/webapp/app/overview/course-card.component.ts +++ b/src/main/webapp/app/overview/course-card.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges } from '@angular/core'; +import { Component, Input, OnChanges, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Color, ScaleType } from '@swimlane/ngx-charts'; import { ARTEMIS_DEFAULT_COLOR } from 'app/app.constants'; @@ -22,10 +22,14 @@ import { RouterLink } from '@angular/router'; selector: 'jhi-overview-course-card', templateUrl: './course-card.component.html', styleUrls: ['course-card.scss'], - standalone: true, imports: [CourseCardHeaderComponent, ArtemisSharedCommonModule, NgxChartsModule, PieChartModule, TranslateDirective, RouterLink], }) export class CourseCardComponent implements OnChanges { + private router = inject(Router); + private route = inject(ActivatedRoute); + private scoresStorageService = inject(ScoresStorageService); + private exerciseService = inject(ExerciseService); + protected readonly faArrowRight = faArrowRight; readonly ARTEMIS_DEFAULT_COLOR = ARTEMIS_DEFAULT_COLOR; @@ -55,13 +59,6 @@ export class CourseCardComponent implements OnChanges { domain: [GraphColors.GREEN, GraphColors.RED], } as Color; - constructor( - private router: Router, - private route: ActivatedRoute, - private scoresStorageService: ScoresStorageService, - private exerciseService: ExerciseService, - ) {} - ngOnChanges() { if (this.course.exercises && this.course.exercises.length > 0) { this.exerciseCount = this.course.exercises.length; diff --git a/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts b/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts index 065a6d802c64..df8dbb0047f6 100644 --- a/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts +++ b/src/main/webapp/app/overview/course-competencies/course-competencies-details.component.ts @@ -1,6 +1,6 @@ import dayjs from 'dayjs/esm'; -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { Competency, CompetencyJol, @@ -26,13 +26,55 @@ import { FeatureToggle, FeatureToggleService } from 'app/shared/feature-toggle/f import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { Course } from 'app/entities/course.model'; import { CourseCompetencyService } from 'app/course/competencies/course-competency.service'; +import { FireworksComponent } from 'app/shared/fireworks/fireworks.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { NgClass } from '@angular/common'; +import { ExerciseUnitComponent } from '../course-lectures/exercise-unit/exercise-unit.component'; +import { AttachmentUnitComponent } from '../course-lectures/attachment-unit/attachment-unit.component'; +import { VideoUnitComponent } from '../course-lectures/video-unit/video-unit.component'; +import { TextUnitComponent } from '../course-lectures/text-unit/text-unit.component'; +import { OnlineUnitComponent } from '../course-lectures/online-unit/online-unit.component'; +import { CompetencyRingsComponent } from '../../course/competencies/competency-rings/competency-rings.component'; +import { SidePanelComponent } from 'app/shared/side-panel/side-panel.component'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-course-competencies-details', templateUrl: './course-competencies-details.component.html', styleUrls: ['../course-overview.scss'], + imports: [ + FireworksComponent, + TranslateDirective, + FaIconComponent, + NgbTooltip, + NgClass, + RouterLink, + ExerciseUnitComponent, + AttachmentUnitComponent, + VideoUnitComponent, + TextUnitComponent, + OnlineUnitComponent, + CompetencyRingsComponent, + SidePanelComponent, + HelpIconComponent, + ArtemisTranslatePipe, + ArtemisTimeAgoPipe, + HtmlForMarkdownPipe, + ], }) export class CourseCompetenciesDetailsComponent implements OnInit, OnDestroy { + private featureToggleService = inject(FeatureToggleService); + private courseStorageService = inject(CourseStorageService); + private alertService = inject(AlertService); + private activatedRoute = inject(ActivatedRoute); + private courseCompetencyService = inject(CourseCompetencyService); + private lectureUnitService = inject(LectureUnitService); + competencyId?: number; course?: Course; courseId?: number; @@ -51,15 +93,6 @@ export class CourseCompetenciesDetailsComponent implements OnInit, OnDestroy { faPencilAlt = faPencilAlt; getIcon = getIcon; - constructor( - private featureToggleService: FeatureToggleService, - private courseStorageService: CourseStorageService, - private alertService: AlertService, - private activatedRoute: ActivatedRoute, - private courseCompetencyService: CourseCompetencyService, - private lectureUnitService: LectureUnitService, - ) {} - ngOnInit(): void { // example route looks like: /courses/1/competencies/10 const courseIdParams$ = this.activatedRoute.parent?.parent?.parent?.params; diff --git a/src/main/webapp/app/overview/course-competencies/course-competencies-details.module.ts b/src/main/webapp/app/overview/course-competencies/course-competencies-details.module.ts index ba6934db7a8c..d9b5bc3d3028 100644 --- a/src/main/webapp/app/overview/course-competencies/course-competencies-details.module.ts +++ b/src/main/webapp/app/overview/course-competencies/course-competencies-details.module.ts @@ -1,3 +1,4 @@ +import { CourseCompetenciesDetailsComponent } from 'app/overview/course-competencies/course-competencies-details.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ArtemisSharedPipesModule } from 'app/shared/pipes/shared-pipes.module'; @@ -8,9 +9,9 @@ import { UserRouteAccessService } from 'app/core/auth/user-route-access-service' import { Authority } from 'app/shared/constants/authority.constants'; import { RouterModule, Routes } from '@angular/router'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; -import { CourseCompetenciesDetailsComponent } from 'app/overview/course-competencies/course-competencies-details.component'; + import { ArtemisSidePanelModule } from 'app/shared/side-panel/side-panel.module'; -import { FireworksModule } from 'app/shared/fireworks/fireworks.module'; + import { JudgementOfLearningRatingComponent } from 'app/course/competencies/judgement-of-learning-rating/judgement-of-learning-rating.component'; import { AttachmentUnitComponent } from 'app/overview/course-lectures/attachment-unit/attachment-unit.component'; import { VideoUnitComponent } from 'app/overview/course-lectures/video-unit/video-unit.component'; @@ -20,7 +21,7 @@ import { OnlineUnitComponent } from 'app/overview/course-lectures/online-unit/on const routes: Routes = [ { path: '', - component: CourseCompetenciesDetailsComponent, + loadComponent: () => import('app/overview/course-competencies/course-competencies-details.component').then((m) => m.CourseCompetenciesDetailsComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.competencies', @@ -38,14 +39,13 @@ const routes: Routes = [ ArtemisCompetenciesModule, ArtemisMarkdownModule, ArtemisSidePanelModule, - FireworksModule, JudgementOfLearningRatingComponent, AttachmentUnitComponent, VideoUnitComponent, TextUnitComponent, OnlineUnitComponent, + CourseCompetenciesDetailsComponent, ], - declarations: [CourseCompetenciesDetailsComponent], exports: [CourseCompetenciesDetailsComponent], }) export class ArtemisCourseCompetenciesDetailsModule {} diff --git a/src/main/webapp/app/overview/course-competencies/course-competencies.component.ts b/src/main/webapp/app/overview/course-competencies/course-competencies.component.ts index 8d15b59fae94..17f7f895a01c 100644 --- a/src/main/webapp/app/overview/course-competencies/course-competencies.component.ts +++ b/src/main/webapp/app/overview/course-competencies/course-competencies.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { AlertService } from 'app/core/util/alert.service'; import { onError } from 'app/shared/util/global.utils'; @@ -10,13 +10,24 @@ import { faAngleDown, faAngleUp } from '@fortawesome/free-solid-svg-icons'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { FeatureToggle, FeatureToggleService } from 'app/shared/feature-toggle/feature-toggle.service'; import { CourseCompetencyService } from 'app/course/competencies/course-competency.service'; +import { CompetencyCardComponent } from '../../course/competencies/competency-card/competency-card.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-course-competencies', templateUrl: './course-competencies.component.html', styleUrls: ['../course-overview.scss'], + imports: [CompetencyCardComponent, FaIconComponent, TranslateDirective, ArtemisTranslatePipe], }) export class CourseCompetenciesComponent implements OnInit, OnDestroy { + private featureToggleService = inject(FeatureToggleService); + private activatedRoute = inject(ActivatedRoute); + private alertService = inject(AlertService); + private courseStorageService = inject(CourseStorageService); + private courseCompetencyService = inject(CourseCompetencyService); + @Input() courseId: number; @@ -35,14 +46,6 @@ export class CourseCompetenciesComponent implements OnInit, OnDestroy { private dashboardFeatureToggleActiveSubscription: Subscription; dashboardFeatureActive = false; - constructor( - private featureToggleService: FeatureToggleService, - private activatedRoute: ActivatedRoute, - private alertService: AlertService, - private courseStorageService: CourseStorageService, - private courseCompetencyService: CourseCompetencyService, - ) {} - ngOnInit(): void { const courseIdParams$ = this.activatedRoute.parent?.parent?.parent?.params; if (courseIdParams$) { diff --git a/src/main/webapp/app/overview/course-competencies/course-competencies.module.ts b/src/main/webapp/app/overview/course-competencies/course-competencies.module.ts index afdcd8e23a2d..42b823fa6996 100644 --- a/src/main/webapp/app/overview/course-competencies/course-competencies.module.ts +++ b/src/main/webapp/app/overview/course-competencies/course-competencies.module.ts @@ -1,12 +1,12 @@ +import { CourseCompetenciesComponent } from 'app/overview/course-competencies/course-competencies.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; -import { CourseCompetenciesComponent } from 'app/overview/course-competencies/course-competencies.component'; + import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { ArtemisCompetenciesModule } from 'app/course/competencies/competency.module'; -import { FireworksModule } from 'app/shared/fireworks/fireworks.module'; const routes: Routes = [ { @@ -16,14 +16,13 @@ const routes: Routes = [ authorities: [Authority.USER], pageTitle: 'overview.competencies', }, - component: CourseCompetenciesComponent, + loadComponent: () => import('app/overview/course-competencies/course-competencies.component').then((m) => m.CourseCompetenciesComponent), canActivate: [UserRouteAccessService], }, ]; @NgModule({ - imports: [RouterModule.forChild(routes), ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule, FireworksModule], - declarations: [CourseCompetenciesComponent], + imports: [RouterModule.forChild(routes), ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule, CourseCompetenciesComponent], exports: [CourseCompetenciesComponent], }) export class CourseCompetenciesModule {} diff --git a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts index 55d32ade492a..5d6bdbfdae2d 100644 --- a/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts +++ b/src/main/webapp/app/overview/course-conversations/code-of-conduct/course-conversations-code-of-conduct.component.ts @@ -1,26 +1,27 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { User } from 'app/core/user/user.model'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { onError } from 'app/shared/util/global.utils'; import { AlertService } from 'app/core/util/alert.service'; import { Course } from 'app/entities/course.model'; import { ConversationService } from 'app/shared/metis/conversations/conversation.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-course-conversations-code-of-conduct', templateUrl: './course-conversations-code-of-conduct.component.html', + imports: [TranslateDirective, HtmlForMarkdownPipe], }) export class CourseConversationsCodeOfConductComponent implements OnInit { + private alertService = inject(AlertService); + private conversationService = inject(ConversationService); + @Input() course: Course; responsibleContacts: User[] = []; - constructor( - private alertService: AlertService, - private conversationService: ConversationService, - ) {} - ngOnInit() { if (this.course.id) { this.conversationService.getResponsibleUsersForCodeOfConduct(this.course.id).subscribe({ diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts index ad2ac6c0a364..1f2828608d82 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.component.ts @@ -1,15 +1,7 @@ +import { NgClass } from '@angular/common'; import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, OnDestroy, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core'; -import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model'; -import { Post } from 'app/entities/metis/post.model'; +import { FormsModule } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { EMPTY, Observable, Subject, Subscription, from, take, takeUntil } from 'rxjs'; -import { catchError, debounceTime, distinctUntilChanged } from 'rxjs/operators'; -import { MetisConversationService } from 'app/shared/metis/metis-conversation.service'; -import { ChannelDTO, ChannelSubType, getAsChannelDTO } from 'app/entities/metis/conversation/channel.model'; -import { MetisService } from 'app/shared/metis/metis.service'; -import { Course, isMessagingEnabled } from 'app/entities/course.model'; -import { PageType, SortDirection } from 'app/shared/metis/metis.util'; import { faBan, faBookmark, @@ -26,22 +18,41 @@ import { faSearch, faTimes, } from '@fortawesome/free-solid-svg-icons'; -import { ButtonType } from 'app/shared/components/button.component'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { UserPublicInfoDTO } from 'app/core/user/user.model'; +import { Course, isMessagingEnabled } from 'app/entities/course.model'; +import { ChannelDTO, ChannelSubType, getAsChannelDTO } from 'app/entities/metis/conversation/channel.model'; +import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model'; +import { Post } from 'app/entities/metis/post.model'; +import { Posting, PostingType, SavedPostStatus, SavedPostStatusMap } from 'app/entities/metis/posting.model'; import { CourseWideSearchComponent, CourseWideSearchConfig } from 'app/overview/course-conversations/course-wide-search/course-wide-search.component'; -import { AccordionGroups, ChannelTypeIcons, CollapseState, SidebarCardElement, SidebarData, SidebarItemShowAlways } from 'app/types/sidebar'; -import { CourseOverviewService } from 'app/overview/course-overview.service'; +import { ChannelsCreateDialogComponent } from 'app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component'; +import { ChannelAction, ChannelsOverviewDialogComponent } from 'app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component'; import { GroupChatCreateDialogComponent } from 'app/overview/course-conversations/dialogs/group-chat-create-dialog/group-chat-create-dialog.component'; -import { defaultFirstLayerDialogOptions, defaultSecondLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util'; -import { UserPublicInfoDTO } from 'app/core/user/user.model'; import { OneToOneChatCreateDialogComponent } from 'app/overview/course-conversations/dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component'; -import { ChannelAction, ChannelsOverviewDialogComponent } from 'app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component'; -import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; -import { ChannelsCreateDialogComponent } from 'app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component'; +import { defaultFirstLayerDialogOptions, defaultSecondLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util'; +import { CourseOverviewService } from 'app/overview/course-overview.service'; import { CourseSidebarService } from 'app/overview/course-sidebar.service'; -import { LayoutService } from 'app/shared/breakpoints/layout.service'; import { CustomBreakpointNames } from 'app/shared/breakpoints/breakpoints.service'; -import { Posting, PostingType, SavedPostStatus, SavedPostStatusMap } from 'app/entities/metis/posting.model'; +import { LayoutService } from 'app/shared/breakpoints/layout.service'; +import { ButtonComponent, ButtonType } from 'app/shared/components/button.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; import { canCreateChannel } from 'app/shared/metis/conversations/conversation-permissions.utils'; +import { MetisConversationService } from 'app/shared/metis/metis-conversation.service'; +import { MetisService } from 'app/shared/metis/metis.service'; +import { PageType, SortDirection } from 'app/shared/metis/metis.util'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { SidebarComponent } from 'app/shared/sidebar/sidebar.component'; +import { AccordionGroups, ChannelTypeIcons, CollapseState, SidebarCardElement, SidebarData, SidebarItemShowAlways } from 'app/types/sidebar'; +import { EMPTY, Observable, Subject, Subscription, from, take, takeUntil } from 'rxjs'; +import { catchError, debounceTime, distinctUntilChanged } from 'rxjs/operators'; +import { CourseConversationsCodeOfConductComponent } from './code-of-conduct/course-conversations-code-of-conduct.component'; +import { ConversationHeaderComponent } from './layout/conversation-header/conversation-header.component'; +import { ConversationMessagesComponent } from './layout/conversation-messages/conversation-messages.component'; +import { ConversationThreadSidebarComponent } from './layout/conversation-thread-sidebar/conversation-thread-sidebar.component'; +import { SavedPostsComponent } from './saved-posts/saved-posts.component'; const DEFAULT_CHANNEL_GROUPS: AccordionGroups = { favoriteChannels: { entityData: [] }, @@ -99,8 +110,31 @@ const DEFAULT_SHOW_ALWAYS: SidebarItemShowAlways = { styleUrls: ['../course-overview.scss', './course-conversations.component.scss'], encapsulation: ViewEncapsulation.None, providers: [MetisService], + imports: [ + LoadingIndicatorContainerComponent, + FormsModule, + ButtonComponent, + CourseConversationsCodeOfConductComponent, + TranslateDirective, + NgClass, + SidebarComponent, + ConversationHeaderComponent, + ConversationMessagesComponent, + CourseWideSearchComponent, + SavedPostsComponent, + ConversationThreadSidebarComponent, + ArtemisTranslatePipe, + ], }) export class CourseConversationsComponent implements OnInit, OnDestroy { + private router = inject(Router); + private activatedRoute = inject(ActivatedRoute); + private metisConversationService = inject(MetisConversationService); + private metisService = inject(MetisService); + private courseOverviewService = inject(CourseOverviewService); + private modalService = inject(NgbModal); + private profileService = inject(ProfileService); + private ngUnsubscribe = new Subject(); private closeSidebarEventSubscription: Subscription; private openSidebarEventSubscription: Subscription; @@ -158,16 +192,6 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { private layoutService: LayoutService = inject(LayoutService); private changeDetector: ChangeDetectorRef = inject(ChangeDetectorRef); - constructor( - private router: Router, - private activatedRoute: ActivatedRoute, - private metisConversationService: MetisConversationService, - private metisService: MetisService, - private courseOverviewService: CourseOverviewService, - private modalService: NgbModal, - private profileService: ProfileService, - ) {} - getAsChannel = getAsChannelDTO; private subscribeToMetis() { @@ -403,8 +427,7 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { complete: () => { this.sidebarConversations = this.courseOverviewService.mapConversationsToSidebarCardElements(this.course!, this.conversationsOfUser); this.accordionConversationGroups = this.courseOverviewService.groupConversationsByChannelType(this.course!, this.conversationsOfUser, this.messagingEnabled); - const currentConversations = this.sidebarConversations?.filter((item) => item.isCurrent) || []; - this.accordionConversationGroups.recents.entityData = currentConversations; + this.accordionConversationGroups.recents.entityData = this.sidebarConversations?.filter((item) => item.isCurrent) || []; this.updateSidebarData(); }, }); diff --git a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts index 10d2cea22583..4a932bb3f54d 100644 --- a/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts +++ b/src/main/webapp/app/overview/course-conversations/course-conversations.module.ts @@ -1,12 +1,12 @@ +import { CourseConversationsComponent } from 'app/overview/course-conversations/course-conversations.component'; import { MetisModule } from 'app/shared/metis/metis.module'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; -import { CourseConversationsComponent } from 'app/overview/course-conversations/course-conversations.component'; + import { ArtemisDataTableModule } from 'app/shared/data-table/data-table.module'; import { ConversationMessagesComponent } from 'app/overview/course-conversations/layout/conversation-messages/conversation-messages.component'; -import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { ConversationThreadSidebarComponent } from 'app/overview/course-conversations/layout/conversation-thread-sidebar/conversation-thread-sidebar.component'; import { ChannelsOverviewDialogComponent } from './dialogs/channels-overview-dialog/channels-overview-dialog.component'; import { ChannelItemComponent } from './dialogs/channels-overview-dialog/channel-item/channel-item.component'; @@ -33,6 +33,7 @@ import { ArtemisSidebarModule } from 'app/shared/sidebar/sidebar.module'; import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; import { SavedPostsComponent } from 'app/overview/course-conversations/saved-posts/saved-posts.component'; import { PostingSummaryComponent } from 'app/overview/course-conversations/posting-summary/posting-summary.component'; +import { InfiniteScrollDirective } from 'ngx-infinite-scroll'; const routes: Routes = [ { @@ -41,7 +42,7 @@ const routes: Routes = [ data: { pageTitle: 'artemisApp.conversationsLayout.tabTitle', }, - component: CourseConversationsComponent, + loadComponent: () => import('app/overview/course-conversations/course-conversations.component').then((m) => m.CourseConversationsComponent), }, ]; @@ -54,11 +55,9 @@ const routes: Routes = [ ArtemisSharedComponentModule, ArtemisDataTableModule, ArtemisSidebarModule, - InfiniteScrollModule, + InfiniteScrollDirective, CourseUsersSelectorModule, ProfilePictureComponent, - ], - declarations: [ CourseConversationsComponent, CourseConversationsCodeOfConductComponent, ConversationThreadSidebarComponent, diff --git a/src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.ts b/src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.ts index 556291d952d1..21a97f3dc9e5 100644 --- a/src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.ts +++ b/src/main/webapp/app/overview/course-conversations/course-wide-search/course-wide-search.component.ts @@ -15,7 +15,7 @@ import { inject, } from '@angular/core'; import { faChevronLeft, faCircleNotch, faEnvelope, faFilter, faLongArrowAltDown, faLongArrowAltUp, faPlus, faTimes } from '@fortawesome/free-solid-svg-icons'; -import { FormBuilder, FormGroup } from '@angular/forms'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Subject, takeUntil } from 'rxjs'; import { Course } from 'app/entities/course.model'; import { ChannelDTO, getAsChannelDTO } from 'app/entities/metis/conversation/channel.model'; @@ -25,14 +25,27 @@ import { MetisConversationService } from 'app/shared/metis/metis-conversation.se import { PostContextFilter, PostSortCriterion, SortDirection } from 'app/shared/metis/metis.util'; import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model'; import { CourseSidebarService } from 'app/overview/course-sidebar.service'; +import { NgClass } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { InfiniteScrollDirective } from 'ngx-infinite-scroll'; +import { PostingThreadComponent } from 'app/shared/metis/posting-thread/posting-thread.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-course-wide-search', templateUrl: './course-wide-search.component.html', styleUrls: ['./course-wide-search.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [NgClass, TranslateDirective, FaIconComponent, FormsModule, ReactiveFormsModule, NgbTooltip, InfiniteScrollDirective, PostingThreadComponent, ArtemisTranslatePipe], }) export class CourseWideSearchComponent implements OnInit, AfterViewInit, OnDestroy { + metisService = inject(MetisService); + metisConversationService = inject(MetisConversationService); + private formBuilder = inject(FormBuilder); + cdr = inject(ChangeDetectorRef); + @Input() courseWideSearchConfig: CourseWideSearchConfig; @@ -74,13 +87,6 @@ export class CourseWideSearchComponent implements OnInit, AfterViewInit, OnDestr private courseSidebarService: CourseSidebarService = inject(CourseSidebarService); - constructor( - public metisService: MetisService, // instance from course-conversations.component - public metisConversationService: MetisConversationService, // instance from course-conversations.component - private formBuilder: FormBuilder, - public cdr: ChangeDetectorRef, - ) {} - ngOnInit() { this.subscribeToMetis(); this.resetFormGroup(); diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/abstract-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/abstract-dialog.component.ts index a42b8aa465ef..4f161f653357 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/abstract-dialog.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/abstract-dialog.component.ts @@ -1,8 +1,9 @@ -import { Directive } from '@angular/core'; +import { Directive, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; @Directive() export abstract class AbstractDialogComponent { + activeModal = inject(NgbActiveModal); isInitialized = false; initialize(requiredInputs?: string[]) { @@ -14,8 +15,6 @@ export abstract class AbstractDialogComponent { } } - constructor(public activeModal: NgbActiveModal) {} - dismiss() { if (this.activeModal) { this.activeModal.dismiss(); diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channel-form/channel-form.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channel-form/channel-form.component.ts index bd471be6ca8b..975f1375e799 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channel-form/channel-form.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channel-form/channel-form.component.ts @@ -1,6 +1,9 @@ -import { Component, EventEmitter, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Component, EventEmitter, OnChanges, OnDestroy, OnInit, Output, inject } from '@angular/core'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ChannelIconComponent } from 'app/overview/course-conversations/other/channel-icon/channel-icon.component'; import { Subject, takeUntil } from 'rxjs'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export interface ChannelFormData { name?: string; @@ -16,8 +19,11 @@ export const channelRegex = new RegExp('^[a-z0-9-]{1}[a-z0-9-]{0,30}$'); @Component({ selector: 'jhi-channel-form', templateUrl: './channel-form.component.html', + imports: [FormsModule, ReactiveFormsModule, TranslateDirective, ChannelIconComponent, ArtemisTranslatePipe], }) export class ChannelFormComponent implements OnInit, OnChanges, OnDestroy { + private fb = inject(FormBuilder); + private ngUnsubscribe = new Subject(); formData: ChannelFormData = { @@ -32,8 +38,6 @@ export class ChannelFormComponent implements OnInit, OnChanges, OnDestroy { form: FormGroup; - constructor(private fb: FormBuilder) {} - get nameControl() { return this.form.get('name'); } @@ -63,7 +67,7 @@ export class ChannelFormComponent implements OnInit, OnChanges, OnDestroy { this.ngUnsubscribe.complete(); } - ngOnChanges(): void { + ngOnChanges() { this.initializeForm(); } diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component.ts index 35c4de065757..14c66dfc94c5 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component.ts @@ -3,14 +3,17 @@ import { ChannelFormData, ChannelType } from 'app/overview/course-conversations/ import { Course } from 'app/entities/course.model'; import { ChannelDTO } from 'app/entities/metis/conversation/channel.model'; import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ChannelFormComponent } from './channel-form/channel-form.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-channels-create-dialog', templateUrl: './channels-create-dialog.component.html', + imports: [TranslateDirective, ChannelFormComponent, ArtemisTranslatePipe], }) export class ChannelsCreateDialogComponent extends AbstractDialogComponent { - @Input() - course: Course; + @Input() course: Course; initialize() { super.initialize(['course']); @@ -19,6 +22,7 @@ export class ChannelsCreateDialogComponent extends AbstractDialogComponent { channelToCreate: ChannelDTO = new ChannelDTO(); isPublicChannel = true; isAnnouncementChannel = false; + onChannelTypeChanged($event: ChannelType) { this.isPublicChannel = $event === 'PUBLIC'; } diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channel-item/channel-item.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channel-item/channel-item.component.ts index 04e21dcc1f35..de0e0c918479 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channel-item/channel-item.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channel-item/channel-item.component.ts @@ -1,12 +1,16 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { ChannelDTO } from 'app/entities/metis/conversation/channel.model'; import { ChannelAction, ChannelActionType } from 'app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component'; +import { ChannelIconComponent } from 'app/overview/course-conversations/other/channel-icon/channel-icon.component'; import { canJoinChannel, canLeaveConversation } from 'app/shared/metis/conversations/conversation-permissions.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-channel-item', templateUrl: './channel-item.component.html', styleUrls: ['./channel-item.component.scss'], + imports: [ChannelIconComponent, TranslateDirective, ArtemisTranslatePipe], }) export class ChannelItemComponent { canJoinChannel = canJoinChannel; diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.ts index deb055e00a5e..45b3bbe9d5c3 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.ts @@ -1,15 +1,17 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { Observable, Subject, debounceTime, distinctUntilChanged, finalize, map, takeUntil } from 'rxjs'; import { faChevronRight } from '@fortawesome/free-solid-svg-icons'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { onError } from 'app/shared/util/global.utils'; import { AlertService } from 'app/core/util/alert.service'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ChannelService } from 'app/shared/metis/conversations/channel.service'; import { ChannelDTO, ChannelSubType } from 'app/entities/metis/conversation/channel.model'; import { Course } from 'app/entities/course.model'; import { canCreateChannel } from 'app/shared/metis/conversations/conversation-permissions.utils'; import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { ChannelItemComponent } from './channel-item/channel-item.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export type ChannelActionType = 'register' | 'deregister' | 'view' | 'create'; export type ChannelAction = { @@ -20,19 +22,19 @@ export type ChannelAction = { selector: 'jhi-channels-overview-dialog', templateUrl: './channels-overview-dialog.component.html', styleUrls: ['./channels-overview-dialog.component.scss'], + imports: [LoadingIndicatorContainerComponent, ChannelItemComponent, ArtemisTranslatePipe], }) export class ChannelsOverviewDialogComponent extends AbstractDialogComponent implements OnInit, OnDestroy { + private channelService = inject(ChannelService); + private alertService = inject(AlertService); + private ngUnsubscribe = new Subject(); canCreateChannel = canCreateChannel; - @Input() - createChannelFn?: (channel: ChannelDTO) => Observable; - - @Input() - course: Course; - @Input() - channelSubType: ChannelSubType; + @Input() createChannelFn?: (channel: ChannelDTO) => Observable; + @Input() course: Course; + @Input() channelSubType: ChannelSubType; channelActions$ = new Subject(); @@ -52,15 +54,6 @@ export class ChannelsOverviewDialogComponent extends AbstractDialogComponent imp } } - constructor( - private channelService: ChannelService, - private alertService: AlertService, - - activeModal: NgbActiveModal, - ) { - super(activeModal); - } - ngOnInit(): void { this.channelActions$.pipe(debounceTime(500), distinctUntilChanged(), takeUntil(this.ngUnsubscribe)).subscribe((channelAction) => { this.performChannelAction(channelAction); diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-add-users-dialog/add-users-form/conversation-add-users-form.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-add-users-dialog/add-users-form/conversation-add-users-form.component.ts index f2cec3a67fc5..7adb84473e9a 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-add-users-dialog/add-users-form/conversation-add-users-form.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-add-users-dialog/add-users-form/conversation-add-users-form.component.ts @@ -1,9 +1,13 @@ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, input } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, inject, input } from '@angular/core'; import { UserPublicInfoDTO } from 'app/core/user/user.model'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model'; import { getAsChannelDTO } from 'app/entities/metis/conversation/channel.model'; import { faSpinner } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CourseUsersSelectorComponent } from 'app/shared/course-users-selector/course-users-selector.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export interface AddUsersFormData { selectedUsers?: UserPublicInfoDTO[]; @@ -16,8 +20,11 @@ export interface AddUsersFormData { @Component({ selector: 'jhi-conversation-add-users-form', templateUrl: './conversation-add-users-form.component.html', + imports: [TranslateDirective, FormsModule, ReactiveFormsModule, CourseUsersSelectorComponent, FaIconComponent, ArtemisTranslatePipe], }) export class ConversationAddUsersFormComponent implements OnInit, OnChanges { + private fb = inject(FormBuilder); + @Output() formSubmitted: EventEmitter = new EventEmitter(); @Input() courseId: number; @@ -34,7 +41,6 @@ export class ConversationAddUsersFormComponent implements OnInit, OnChanges { getAsChannel = getAsChannelDTO; mode: 'individual' | 'group' = 'individual'; - constructor(private fb: FormBuilder) {} get selectedUsersControl() { return this.form.get('selectedUsers'); @@ -52,7 +58,7 @@ export class ConversationAddUsersFormComponent implements OnInit, OnChanges { this.initializeForm(); } - ngOnChanges(): void { + ngOnChanges() { this.initializeForm(); } diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-add-users-dialog/conversation-add-users-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-add-users-dialog/conversation-add-users-dialog.component.ts index 3813b3939fa6..e1a062a62aeb 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-add-users-dialog/conversation-add-users-dialog.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-add-users-dialog/conversation-add-users-dialog.component.ts @@ -1,6 +1,5 @@ -import { Component, Input, OnDestroy } from '@angular/core'; +import { Component, Input, OnDestroy, inject } from '@angular/core'; import { AlertService } from 'app/core/util/alert.service'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AddUsersFormData } from 'app/overview/course-conversations/dialogs/conversation-add-users-dialog/add-users-form/conversation-add-users-form.component'; import { UserPublicInfoDTO } from 'app/core/user/user.model'; import { Course } from 'app/entities/course.model'; @@ -16,12 +15,22 @@ import { GroupChatService } from 'app/shared/metis/conversations/group-chat.serv import { Subject, takeUntil } from 'rxjs'; import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; import { finalize } from 'rxjs/operators'; +import { ChannelIconComponent } from '../../other/channel-icon/channel-icon.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ConversationAddUsersFormComponent } from './add-users-form/conversation-add-users-form.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-conversation-add-users-dialog', templateUrl: './conversation-add-users-dialog.component.html', + imports: [ChannelIconComponent, TranslateDirective, ConversationAddUsersFormComponent, ArtemisTranslatePipe], }) export class ConversationAddUsersDialogComponent extends AbstractDialogComponent implements OnDestroy { + private alertService = inject(AlertService); + channelService = inject(ChannelService); + conversationService = inject(ConversationService); + groupChatService = inject(GroupChatService); + private ngUnsubscribe = new Subject(); @Input() course: Course; @@ -29,7 +38,7 @@ export class ConversationAddUsersDialogComponent extends AbstractDialogComponent isInitialized = false; maxSelectable: number | undefined; - protected isLoading: boolean = false; + protected isLoading = false; initialize() { super.initialize(['course', 'activeConversation']); @@ -40,17 +49,6 @@ export class ConversationAddUsersDialogComponent extends AbstractDialogComponent } } - constructor( - private alertService: AlertService, - - activeModal: NgbActiveModal, - public channelService: ChannelService, - public conversationService: ConversationService, - public groupChatService: GroupChatService, - ) { - super(activeModal); - } - ngOnDestroy() { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/conversation-detail-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/conversation-detail-dialog.component.ts index bd75071f1610..2092c6c13794 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/conversation-detail-dialog.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/conversation-detail-dialog.component.ts @@ -1,13 +1,19 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model'; import { Course } from 'app/entities/course.model'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { getAsChannelDTO } from 'app/entities/metis/conversation/channel.model'; import { ConversationService } from 'app/shared/metis/conversations/conversation.service'; import { isOneToOneChatDTO } from 'app/entities/metis/conversation/one-to-one-chat.model'; import { getAsGroupChatDTO } from 'app/entities/metis/conversation/group-chat.model'; import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; import { faPeopleGroup } from '@fortawesome/free-solid-svg-icons'; +import { ChannelIconComponent } from '../../other/channel-icon/channel-icon.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { RouterLink } from '@angular/router'; +import { ConversationMembersComponent } from './tabs/conversation-members/conversation-members.component'; +import { ConversationInfoComponent } from './tabs/conversation-info/conversation-info.component'; +import { ConversationSettingsComponent } from './tabs/conversation-settings/conversation-settings.component'; export enum ConversationDetailTabs { MEMBERS = 'members', @@ -18,8 +24,11 @@ export enum ConversationDetailTabs { @Component({ selector: 'jhi-conversation-detail-dialog', templateUrl: './conversation-detail-dialog.component.html', + imports: [ChannelIconComponent, FaIconComponent, TranslateDirective, RouterLink, ConversationMembersComponent, ConversationInfoComponent, ConversationSettingsComponent], }) export class ConversationDetailDialogComponent extends AbstractDialogComponent { + conversationService = inject(ConversationService); + @Input() public activeConversation: ConversationDTO; @Input() course: Course; @Input() selectedTab: ConversationDetailTabs = ConversationDetailTabs.MEMBERS; @@ -38,12 +47,6 @@ export class ConversationDetailDialogComponent extends AbstractDialogComponent { changesWerePerformed = false; Tabs = ConversationDetailTabs; - constructor( - activeModal: NgbActiveModal, - public conversationService: ConversationService, - ) { - super(activeModal); - } clear() { if (this.changesWerePerformed) { diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-info/conversation-info.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-info/conversation-info.component.ts index 496322be93a6..e645cf032673 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-info/conversation-info.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-info/conversation-info.component.ts @@ -20,11 +20,15 @@ import { GroupChatDTO, getAsGroupChatDTO, isGroupChatDTO } from 'app/entities/me import { GroupChatService } from 'app/shared/metis/conversations/group-chat.service'; import { catchError } from 'rxjs/operators'; import { ConversationUserDTO } from 'app/entities/metis/conversation/conversation-user-dto.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-conversation-info', templateUrl: './conversation-info.component.html', styleUrls: ['./conversation-info.component.scss'], + imports: [TranslateDirective, ArtemisDatePipe, ArtemisTranslatePipe], }) export class ConversationInfoComponent implements OnInit, OnDestroy { private ngUnsubscribe = new Subject(); diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.ts index 9112db6a5021..513312fe34a2 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-member-row/conversation-member-row.component.ts @@ -3,7 +3,7 @@ import { faEllipsis, faUser, faUserCheck, faUserGear, faUserGraduate } from '@fo import { User } from 'app/core/user/user.model'; import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model'; import { AccountService } from 'app/core/auth/account.service'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { NgbDropdown, NgbDropdownButtonItem, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle, NgbModal, NgbModalRef, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { EMPTY, Observable, Subject, from, takeUntil } from 'rxjs'; import { Course } from 'app/entities/course.model'; import { canGrantChannelModeratorRole, canRemoveUsersFromConversation, canRevokeChannelModeratorRole } from 'app/shared/metis/conversations/conversation-permissions.utils'; @@ -20,12 +20,27 @@ import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { getAsGroupChatDTO, isGroupChatDTO } from 'app/entities/metis/conversation/group-chat.model'; import { GroupChatService } from 'app/shared/metis/conversations/group-chat.service'; import { catchError } from 'rxjs/operators'; +import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ - // eslint-disable-next-line @angular-eslint/component-selector selector: '[jhi-conversation-member-row]', templateUrl: './conversation-member-row.component.html', styleUrls: ['./conversation-member-row.component.scss'], + imports: [ + ProfilePictureComponent, + FaIconComponent, + NgbTooltip, + NgbDropdown, + NgbDropdownToggle, + NgbDropdownMenu, + NgbDropdownButtonItem, + NgbDropdownItem, + TranslateDirective, + ArtemisTranslatePipe, + ], }) export class ConversationMemberRowComponent implements OnInit, OnDestroy { private ngUnsubscribe = new Subject(); diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-members.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-members.component.ts index 4164cce5f8ca..c4757f1e2622 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-members.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-members/conversation-members.component.ts @@ -8,12 +8,18 @@ import { AlertService } from 'app/core/util/alert.service'; import { onError } from 'app/shared/util/global.utils'; import { EMPTY, Subject, from, map } from 'rxjs'; import { faMagnifyingGlass, faUserPlus } from '@fortawesome/free-solid-svg-icons'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbModalRef, NgbPagination } from '@ng-bootstrap/ng-bootstrap'; import { ConversationAddUsersDialogComponent } from 'app/overview/course-conversations/dialogs/conversation-add-users-dialog/conversation-add-users-dialog.component'; import { getAsChannelDTO, isChannelDTO } from 'app/entities/metis/conversation/channel.model'; import { canAddUsersToConversation } from 'app/shared/metis/conversations/conversation-permissions.utils'; import { ConversationUserDTO } from 'app/entities/metis/conversation/conversation-user-dto.model'; import { defaultSecondLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { ConversationMemberRowComponent } from './conversation-member-row/conversation-member-row.component'; +import { ItemCountComponent } from 'app/shared/pagination/item-count.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; interface SearchQuery { searchTerm: string; @@ -22,6 +28,7 @@ interface SearchQuery { @Component({ selector: 'jhi-conversation-members', templateUrl: './conversation-members.component.html', + imports: [FaIconComponent, TranslateDirective, FormsModule, ConversationMemberRowComponent, ItemCountComponent, NgbPagination, ArtemisTranslatePipe], }) export class ConversationMembersComponent implements OnInit, OnDestroy { private ngUnsubscribe = new Subject(); diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-settings/conversation-settings.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-settings/conversation-settings.component.ts index e9e5743b66d7..738c02c9298e 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-settings/conversation-settings.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-settings/conversation-settings.component.ts @@ -15,11 +15,15 @@ import { GroupChatService } from 'app/shared/metis/conversations/group-chat.serv import { isGroupChatDTO } from 'app/entities/metis/conversation/group-chat.model'; import { defaultSecondLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util'; import { catchError } from 'rxjs/operators'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-conversation-settings', templateUrl: './conversation-settings.component.html', styleUrls: ['./conversation-settings.component.scss'], + imports: [TranslateDirective, DeleteButtonDirective, FaIconComponent], }) export class ConversationSettingsComponent implements OnInit, OnDestroy { private ngUnsubscribe = new Subject(); diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/generic-confirmation-dialog/generic-confirmation-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/generic-confirmation-dialog/generic-confirmation-dialog.component.ts index 7389eb84cf83..bb9b1c5bef48 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/generic-confirmation-dialog/generic-confirmation-dialog.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/generic-confirmation-dialog/generic-confirmation-dialog.component.ts @@ -1,6 +1,7 @@ import { Component, Input } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export interface GenericConfirmationTranslationKeys { titleKey: string; @@ -11,23 +12,13 @@ export interface GenericConfirmationTranslationKeys { @Component({ selector: 'jhi-generic-confirmation-dialog', templateUrl: './generic-confirmation-dialog.component.html', + imports: [TranslateDirective, ArtemisTranslatePipe], }) export class GenericConfirmationDialogComponent extends AbstractDialogComponent { - @Input() - translationParameters = {}; - - @Input() - translationKeys: GenericConfirmationTranslationKeys; - - @Input() - canBeUndone = true; - - @Input() - isDangerousAction = false; - - constructor(activeModal: NgbActiveModal) { - super(activeModal); - } + @Input() translationParameters = {}; + @Input() translationKeys: GenericConfirmationTranslationKeys; + @Input() canBeUndone = true; + @Input() isDangerousAction = false; initialize() { super.initialize(['translationKeys']); diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/generic-update-text-property-dialog/generic-update-text-property-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/generic-update-text-property-dialog/generic-update-text-property-dialog.component.ts index 6df266a1fbde..c1ca7c07295c 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/generic-update-text-property-dialog/generic-update-text-property-dialog.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/generic-update-text-property-dialog/generic-update-text-property-dialog.component.ts @@ -1,8 +1,9 @@ -import { Component, Input } from '@angular/core'; -import { FormGroup, Validators } from '@angular/forms'; +import { Component, Input, inject } from '@angular/core'; +import { FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { FormBuilder } from '@angular/forms'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export interface GenericUpdateTextPropertyTranslationKeys { labelKey: string; @@ -20,25 +21,18 @@ export interface GenericUpdateTextPropertyTranslationKeys { @Component({ selector: 'jhi-generic-update-text-property-dialog', templateUrl: './generic-update-text-property-dialog.component.html', + imports: [FormsModule, ReactiveFormsModule, TranslateDirective, ArtemisTranslatePipe], }) export class GenericUpdateTextPropertyDialogComponent extends AbstractDialogComponent { - @Input() - propertyName: string; + private fb = inject(FormBuilder); - @Input() - isRequired = false; + @Input() propertyName: string; + @Input() isRequired = false; + @Input() regexPattern: RegExp | undefined; + @Input() maxPropertyLength: number; + @Input() initialValue: string | undefined; + @Input() translationKeys: GenericUpdateTextPropertyTranslationKeys; - @Input() - regexPattern: RegExp | undefined; - - @Input() - maxPropertyLength: number; - - @Input() - initialValue: string | undefined; - - @Input() - translationKeys: GenericUpdateTextPropertyTranslationKeys; form: FormGroup; initialize() { @@ -55,12 +49,7 @@ export class GenericUpdateTextPropertyDialogComponent extends AbstractDialogComp get control() { return this.form.get(this.propertyName); } - constructor( - private fb: FormBuilder, - activeModal: NgbActiveModal, - ) { - super(activeModal); - } + private initializeForm() { if (this.form) { return; diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/group-chat-create-dialog/group-chat-create-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/group-chat-create-dialog/group-chat-create-dialog.component.ts index c694f953f9a3..20ded5b205a0 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/group-chat-create-dialog/group-chat-create-dialog.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/group-chat-create-dialog/group-chat-create-dialog.component.ts @@ -1,24 +1,21 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { Course } from 'app/entities/course.model'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CourseUsersSelectorComponent } from 'app/shared/course-users-selector/course-users-selector.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-group-chat-create-dialog', templateUrl: './group-chat-create-dialog.component.html', + imports: [TranslateDirective, FormsModule, ReactiveFormsModule, CourseUsersSelectorComponent, ArtemisTranslatePipe], }) export class GroupChatCreateDialogComponent extends AbstractDialogComponent { - @Input() - course: Course; - form: FormGroup; + private fb = inject(FormBuilder); - constructor( - activeModal: NgbActiveModal, - private fb: FormBuilder, - ) { - super(activeModal); - } + @Input() course: Course; + form: FormGroup; initialize() { super.initialize(['course']); diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component.ts b/src/main/webapp/app/overview/course-conversations/dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component.ts index 669a7b4027f7..e113fe358f0a 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component.ts +++ b/src/main/webapp/app/overview/course-conversations/dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component.ts @@ -1,24 +1,24 @@ import { Component, Input } from '@angular/core'; import { Course } from 'app/entities/course.model'; import { UserPublicInfoDTO } from 'app/core/user/user.model'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CourseUsersSelectorComponent } from 'app/shared/course-users-selector/course-users-selector.component'; +import { FormsModule } from '@angular/forms'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-one-to-one-chat-create-dialog', templateUrl: './one-to-one-chat-create-dialog.component.html', + imports: [TranslateDirective, CourseUsersSelectorComponent, FormsModule, ArtemisTranslatePipe], }) export class OneToOneChatCreateDialogComponent extends AbstractDialogComponent { - @Input() - course: Course; + @Input() course: Course; isInitialized = false; selectedUsers: UserPublicInfoDTO[] = []; userToChatWith?: UserPublicInfoDTO; - constructor(activeModal: NgbActiveModal) { - super(activeModal); - } initialize() { super.initialize(['course']); } diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-header/conversation-header.component.ts b/src/main/webapp/app/overview/course-conversations/layout/conversation-header/conversation-header.component.ts index a8c9ff8ffe26..83f7caf63f97 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-header/conversation-header.component.ts +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-header/conversation-header.component.ts @@ -20,13 +20,24 @@ import { MetisService } from 'app/shared/metis/metis.service'; import { CourseSidebarService } from 'app/overview/course-sidebar.service'; import { getAsOneToOneChatDTO } from 'app/entities/metis/conversation/one-to-one-chat.model'; import { ConversationUserDTO } from 'app/entities/metis/conversation/conversation-user-dto.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ChannelIconComponent } from '../../other/channel-icon/channel-icon.component'; +import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { RouterLink } from '@angular/router'; @Component({ selector: 'jhi-conversation-header', templateUrl: './conversation-header.component.html', styleUrls: ['./conversation-header.component.scss'], + imports: [FaIconComponent, ChannelIconComponent, ProfilePictureComponent, TranslateDirective, RouterLink], }) export class ConversationHeaderComponent implements OnInit, OnDestroy { + private modalService = inject(NgbModal); + metisConversationService = inject(MetisConversationService); + conversationService = inject(ConversationService); + private metisService = inject(MetisService); + private ngUnsubscribe = new Subject(); @Output() collapseSearch = new EventEmitter(); @@ -51,14 +62,6 @@ export class ConversationHeaderComponent implements OnInit, OnDestroy { private courseSidebarService: CourseSidebarService = inject(CourseSidebarService); - constructor( - private modalService: NgbModal, - // instantiated at course-conversation.component.ts - public metisConversationService: MetisConversationService, - public conversationService: ConversationService, - private metisService: MetisService, - ) {} - getAsGroupChat = getAsGroupChatDTO; getAsOneToOneChat = getAsOneToOneChatDTO; diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts index 81e5b88d053c..ac94e22cf0fa 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts @@ -9,7 +9,6 @@ import { OnInit, Output, QueryList, - Renderer2, ViewChild, ViewChildren, ViewEncapsulation, @@ -36,6 +35,13 @@ import { CustomBreakpointNames } from 'app/shared/breakpoints/breakpoints.servic import dayjs from 'dayjs/esm'; import { User } from 'app/core/user/user.model'; import { PostingThreadComponent } from 'app/shared/metis/posting-thread/posting-thread.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { InfiniteScrollDirective } from 'ngx-infinite-scroll'; +import { NgClass } from '@angular/common'; +import { PostCreateEditModalComponent } from 'app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component'; +import { MessageInlineInputComponent } from 'app/shared/metis/message/message-inline-input/message-inline-input.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; interface PostGroup { author: User | undefined; @@ -47,8 +53,22 @@ interface PostGroup { templateUrl: './conversation-messages.component.html', styleUrls: ['./conversation-messages.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + FaIconComponent, + TranslateDirective, + InfiniteScrollDirective, + NgClass, + PostingThreadComponent, + PostCreateEditModalComponent, + MessageInlineInputComponent, + ArtemisTranslatePipe, + ], }) export class ConversationMessagesComponent implements OnInit, AfterViewInit, OnDestroy { + metisService = inject(MetisService); + metisConversationService = inject(MetisConversationService); + cdr = inject(ChangeDetectorRef); + private ngUnsubscribe = new Subject(); readonly sessionStorageKey = 'conversationId.scrollPosition.'; @@ -62,19 +82,14 @@ export class ConversationMessagesComponent implements OnInit, AfterViewInit, OnD @Output() openThread = new EventEmitter(); - @ViewChild('searchInput') - searchInput: ElementRef; - - @ViewChildren('postingThread') - messages: QueryList; - @ViewChild('container') - content: ElementRef; - @Input() - course?: Course; - @Input() - searchbarCollapsed = false; - @Input() - contentHeightDev: boolean = false; + @ViewChild('searchInput') searchInput: ElementRef; + @ViewChildren('postingThread') messages: QueryList; + @ViewChild('container') content: ElementRef; + + @Input() course?: Course; + @Input() searchbarCollapsed = false; + @Input() contentHeightDev = false; + readonly focusPostId = input(undefined); readonly openThreadOnFocus = input(false); @@ -106,16 +121,11 @@ export class ConversationMessagesComponent implements OnInit, AfterViewInit, OnD isHiddenInputWithCallToAction = false; isHiddenInputFull = false; focusOnPostId: number | undefined = undefined; - isOpenThreadOnFocus: boolean = false; + isOpenThreadOnFocus = false; private layoutService: LayoutService = inject(LayoutService); - private renderer = inject(Renderer2); - constructor( - public metisService: MetisService, // instance from course-conversations.component - public metisConversationService: MetisConversationService, // instance from course-conversations.component - public cdr: ChangeDetectorRef, - ) { + constructor() { effect(() => { this.focusOnPostId = this.focusPostId(); this.isOpenThreadOnFocus = this.openThreadOnFocus(); @@ -365,9 +375,9 @@ export class ConversationMessagesComponent implements OnInit, AfterViewInit, OnD return this.metisService.createEmptyPostForContext(conversation); } - postsGroupTrackByFn = (index: number, post: PostGroup): string => 'grp_' + post.posts.map((p) => p.id?.toString()).join('_'); + postsGroupTrackByFn = (_index: number, post: PostGroup): string => 'grp_' + post.posts.map((p) => p.id?.toString()).join('_'); - postsTrackByFn = (index: number, post: Post): string => 'post_' + post.id!; + postsTrackByFn = (_index: number, post: Post): string => 'post_' + post.id!; setPostForThread(post: Post) { this.openThread.emit(post); diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-thread-sidebar/conversation-thread-sidebar.component.ts b/src/main/webapp/app/overview/course-conversations/layout/conversation-thread-sidebar/conversation-thread-sidebar.component.ts index 2307a9f9d182..780198d01f2a 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-thread-sidebar/conversation-thread-sidebar.component.ts +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-thread-sidebar/conversation-thread-sidebar.component.ts @@ -5,11 +5,18 @@ import { faArrowLeft, faChevronLeft, faGripLinesVertical, faXmark } from '@forta import { AnswerPost } from 'app/entities/metis/answer-post.model'; import { Conversation, ConversationDTO } from 'app/entities/metis/conversation/conversation.model'; import { getAsChannelDTO } from 'app/entities/metis/conversation/channel.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { PostComponent } from 'app/shared/metis/post/post.component'; +import { MessageReplyInlineInputComponent } from 'app/shared/metis/message/message-reply-inline-input/message-reply-inline-input.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-conversation-thread-sidebar', templateUrl: './conversation-thread-sidebar.component.html', styleUrls: ['./conversation-thread-sidebar.component.scss'], + imports: [FaIconComponent, TranslateDirective, NgbTooltip, PostComponent, MessageReplyInlineInputComponent, ArtemisTranslatePipe], }) export class ConversationThreadSidebarComponent implements AfterViewInit { @ViewChild('scrollBody', { static: false }) scrollBody?: ElementRef; diff --git a/src/main/webapp/app/overview/course-conversations/other/channel-icon/channel-icon.component.ts b/src/main/webapp/app/overview/course-conversations/other/channel-icon/channel-icon.component.ts index e42fa8358454..6dda4bb3f70b 100644 --- a/src/main/webapp/app/overview/course-conversations/other/channel-icon/channel-icon.component.ts +++ b/src/main/webapp/app/overview/course-conversations/other/channel-icon/channel-icon.component.ts @@ -1,9 +1,11 @@ import { Component, Input } from '@angular/core'; import { faBoxArchive, faBullhorn, faHashtag, faLock } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-channel-icon', templateUrl: './channel-icon.component.html', + imports: [FaIconComponent], }) export class ChannelIconComponent { @Input() diff --git a/src/main/webapp/app/overview/course-conversations/posting-summary/posting-summary.component.ts b/src/main/webapp/app/overview/course-conversations/posting-summary/posting-summary.component.ts index 6e5b23539180..ed80391c59cc 100644 --- a/src/main/webapp/app/overview/course-conversations/posting-summary/posting-summary.component.ts +++ b/src/main/webapp/app/overview/course-conversations/posting-summary/posting-summary.component.ts @@ -3,11 +3,20 @@ import { Posting, PostingType, SavedPostStatus } from 'app/entities/metis/postin import { faBookmark, faBoxArchive, faCheckSquare, faEllipsis, faHashtag, faLock } from '@fortawesome/free-solid-svg-icons'; import { ConversationType } from 'app/entities/metis/conversation/conversation.model'; import dayjs from 'dayjs/esm'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass } from '@angular/common'; +import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { PostingContentComponent } from 'app/shared/metis/posting-content/posting-content.components'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-posting-summary', templateUrl: './posting-summary.component.html', styleUrls: ['./posting-summary.component.scss'], + imports: [FaIconComponent, TranslateDirective, NgClass, ProfilePictureComponent, NgbTooltip, PostingContentComponent, ArtemisDatePipe, ArtemisTranslatePipe], }) export class PostingSummaryComponent { readonly post = input(); diff --git a/src/main/webapp/app/overview/course-conversations/saved-posts/saved-posts.component.ts b/src/main/webapp/app/overview/course-conversations/saved-posts/saved-posts.component.ts index f3606114b830..ca1c0306f385 100644 --- a/src/main/webapp/app/overview/course-conversations/saved-posts/saved-posts.component.ts +++ b/src/main/webapp/app/overview/course-conversations/saved-posts/saved-posts.component.ts @@ -2,11 +2,15 @@ import { Component, effect, inject, input, output } from '@angular/core'; import { Posting, SavedPostStatus, SavedPostStatusMap } from 'app/entities/metis/posting.model'; import { SavedPostService } from 'app/shared/metis/saved-post.service'; import { faBookmark, faInfoCircle } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { PostingSummaryComponent } from '../posting-summary/posting-summary.component'; @Component({ selector: 'jhi-saved-posts', templateUrl: './saved-posts.component.html', styleUrls: ['./saved-posts.component.scss'], + imports: [TranslateDirective, FaIconComponent, PostingSummaryComponent], }) export class SavedPostsComponent { readonly savedPostStatus = input(); @@ -19,7 +23,7 @@ export class SavedPostsComponent { protected posts: Posting[]; protected hiddenPosts: number[] = []; protected savedPostStatusMap: SavedPostStatusMap = SavedPostStatusMap.PROGRESS; - protected isShowDeleteNotice: boolean = false; + protected isShowDeleteNotice = false; // Icons readonly faBookmark = faBookmark; diff --git a/src/main/webapp/app/overview/course-dashboard/course-dashboard.component.ts b/src/main/webapp/app/overview/course-dashboard/course-dashboard.component.ts index 469ec9257996..9b54d2eb1616 100644 --- a/src/main/webapp/app/overview/course-dashboard/course-dashboard.component.ts +++ b/src/main/webapp/app/overview/course-dashboard/course-dashboard.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core'; +import { Component, ElementRef, OnDestroy, OnInit, QueryList, ViewChildren, inject } from '@angular/core'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { Subscription } from 'rxjs'; import { ActivatedRoute, Router } from '@angular/router'; @@ -18,13 +18,38 @@ import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { PROFILE_IRIS } from 'app/app.constants'; import { CompetencyAccordionToggleEvent } from 'app/course/competencies/competency-accordion/competency-accordion.component'; import { AccountService } from 'app/core/auth/account.service'; +import { CourseChatbotComponent } from '../../iris/course-chatbot/course-chatbot.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbProgressbar } from '@ng-bootstrap/ng-bootstrap'; +import { CourseExercisePerformanceComponent } from './course-exercise-performance/course-exercise-performance.component'; +import { CourseExerciseLatenessComponent } from './course-exercise-lateness/course-exercise-lateness.component'; +import { CompetencyAccordionComponent } from '../../course/competencies/competency-accordion/competency-accordion.component'; +import { FeatureToggleHideDirective } from 'app/shared/feature-toggle/feature-toggle-hide.directive'; @Component({ selector: 'jhi-course-dashboard', templateUrl: './course-dashboard.component.html', styleUrls: ['./course-dashboard.component.scss'], + imports: [ + CourseChatbotComponent, + TranslateDirective, + NgbProgressbar, + CourseExercisePerformanceComponent, + CourseExerciseLatenessComponent, + CompetencyAccordionComponent, + FeatureToggleHideDirective, + ], }) export class CourseDashboardComponent implements OnInit, OnDestroy { + private courseStorageService = inject(CourseStorageService); + private alertService = inject(AlertService); + private route = inject(ActivatedRoute); + private router = inject(Router); + private courseDashboardService = inject(CourseDashboardService); + private irisSettingsService = inject(IrisSettingsService); + private profileService = inject(ProfileService); + private accountService = inject(AccountService); + courseId: number; exerciseId: number; points: number = 0; @@ -52,17 +77,6 @@ export class CourseDashboardComponent implements OnInit, OnDestroy { @ViewChildren('competencyAccordionElement', { read: ElementRef }) competencyAccordions: QueryList; - constructor( - private courseStorageService: CourseStorageService, - private alertService: AlertService, - private route: ActivatedRoute, - private router: Router, - private courseDashboardService: CourseDashboardService, - private irisSettingsService: IrisSettingsService, - private profileService: ProfileService, - private accountService: AccountService, - ) {} - ngOnInit(): void { this.paramSubscription = this.route.parent?.parent?.params.subscribe((params) => { this.courseId = parseInt(params['courseId'], 10); diff --git a/src/main/webapp/app/overview/course-dashboard/course-dashboard.module.ts b/src/main/webapp/app/overview/course-dashboard/course-dashboard.module.ts index 834b3db16f4a..126431958157 100644 --- a/src/main/webapp/app/overview/course-dashboard/course-dashboard.module.ts +++ b/src/main/webapp/app/overview/course-dashboard/course-dashboard.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { CourseDashboardComponent } from 'app/overview/course-dashboard/course-dashboard.component'; + import { ArtemisSharedModule } from 'app/shared/shared.module'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { RouterModule, Routes } from '@angular/router'; @@ -14,14 +15,14 @@ import { ArtemisSharedComponentModule } from 'app/shared/components/shared-compo import { NgxDatatableModule } from '@siemens/ngx-datatable'; import { LineChartModule } from '@swimlane/ngx-charts'; import { IrisModule } from 'app/iris/iris.module'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { ArtemisCompetenciesModule } from 'app/course/competencies/competency.module'; const routes: Routes = [ { path: '', pathMatch: 'full', - component: CourseDashboardComponent, + loadComponent: () => import('app/overview/course-dashboard/course-dashboard.component').then((m) => m.CourseDashboardComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.dashboard', @@ -31,7 +32,6 @@ const routes: Routes = [ ]; @NgModule({ - declarations: [CourseDashboardComponent, CourseExercisePerformanceComponent, CourseExerciseLatenessComponent], exports: [CourseDashboardComponent], imports: [ CommonModule, @@ -44,8 +44,10 @@ const routes: Routes = [ ArtemisSharedComponentModule, LineChartModule, IrisModule, - FeatureToggleModule, ArtemisCompetenciesModule, + CourseDashboardComponent, + CourseExercisePerformanceComponent, + CourseExerciseLatenessComponent, ], }) export class CourseDashboardModule {} diff --git a/src/main/webapp/app/overview/course-dashboard/course-dashboard.service.ts b/src/main/webapp/app/overview/course-dashboard/course-dashboard.service.ts index a6a1e09c0750..daf177e123ef 100644 --- a/src/main/webapp/app/overview/course-dashboard/course-dashboard.service.ts +++ b/src/main/webapp/app/overview/course-dashboard/course-dashboard.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable, map } from 'rxjs'; import { CompetencyMetrics, ExerciseInformation, LectureUnitInformation, StudentMetrics } from 'app/entities/student-metrics.model'; @@ -10,9 +10,9 @@ import { CompetencyJol } from 'app/entities/competency.model'; @Injectable({ providedIn: 'root' }) export class CourseDashboardService { - public resourceUrl = 'api/metrics'; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + public resourceUrl = 'api/metrics'; getCourseMetricsForUser(courseId: number): Observable> { return this.http.get(`${this.resourceUrl}/course/${courseId}/student`, { observe: 'response' }).pipe( diff --git a/src/main/webapp/app/overview/course-dashboard/course-exercise-lateness/course-exercise-lateness.component.ts b/src/main/webapp/app/overview/course-dashboard/course-exercise-lateness/course-exercise-lateness.component.ts index 0192a35fff98..8a3a741b48cc 100644 --- a/src/main/webapp/app/overview/course-dashboard/course-exercise-lateness/course-exercise-lateness.component.ts +++ b/src/main/webapp/app/overview/course-dashboard/course-exercise-lateness/course-exercise-lateness.component.ts @@ -1,10 +1,13 @@ -import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnDestroy, OnInit, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { Color, ScaleType } from '@swimlane/ngx-charts'; +import { Color, LineChartModule, ScaleType } from '@swimlane/ngx-charts'; import { GraphColors } from 'app/entities/statistics.model'; import { NgxChartsMultiSeriesDataEntry } from 'app/shared/chart/ngx-charts-datatypes'; import { round } from 'app/shared/util/utils'; import { Subscription } from 'rxjs'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export interface ExerciseLateness { exerciseId: number; @@ -21,8 +24,11 @@ const AVERAGE_GRAPH_COLOR = GraphColors.YELLOW; selector: 'jhi-course-exercise-lateness', templateUrl: './course-exercise-lateness.component.html', styleUrls: ['./course-exercise-lateness.component.scss'], + imports: [TranslateDirective, HelpIconComponent, LineChartModule, ArtemisTranslatePipe], }) export class CourseExerciseLatenessComponent implements OnInit, OnChanges, OnDestroy { + private translateService = inject(TranslateService); + @Input() exerciseLateness: ExerciseLateness[] = []; yourLatenessLabel: string; @@ -42,7 +48,7 @@ export class CourseExerciseLatenessComponent implements OnInit, OnChanges, OnDes protected readonly YOUR_GRAPH_COLOR = YOUR_GRAPH_COLOR; protected readonly AVERAGE_GRAPH_COLOR = AVERAGE_GRAPH_COLOR; - constructor(private translateService: TranslateService) { + constructor() { this.translateServiceSubscription = this.translateService.onLangChange.subscribe(() => { this.setupChart(); }); @@ -56,7 +62,7 @@ export class CourseExerciseLatenessComponent implements OnInit, OnChanges, OnDes this.translateServiceSubscription.unsubscribe(); } - ngOnChanges(): void { + ngOnChanges() { this.setupChart(); } diff --git a/src/main/webapp/app/overview/course-dashboard/course-exercise-performance/course-exercise-performance.component.ts b/src/main/webapp/app/overview/course-dashboard/course-exercise-performance/course-exercise-performance.component.ts index 127b4ad92bb7..5168ee937684 100644 --- a/src/main/webapp/app/overview/course-dashboard/course-exercise-performance/course-exercise-performance.component.ts +++ b/src/main/webapp/app/overview/course-dashboard/course-exercise-performance/course-exercise-performance.component.ts @@ -1,10 +1,13 @@ -import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnDestroy, OnInit, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { Color, ScaleType } from '@swimlane/ngx-charts'; +import { Color, LineChartModule, ScaleType } from '@swimlane/ngx-charts'; import { GraphColors } from 'app/entities/statistics.model'; import { NgxChartsMultiSeriesDataEntry } from 'app/shared/chart/ngx-charts-datatypes'; import { round } from 'app/shared/util/utils'; import { Subscription } from 'rxjs'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export interface ExercisePerformance { exerciseId: number; @@ -21,8 +24,11 @@ const AVERAGE_GRAPH_COLOR = GraphColors.YELLOW; selector: 'jhi-course-exercise-performance', templateUrl: './course-exercise-performance.component.html', styleUrls: ['./course-exercise-performance.component.scss'], + imports: [TranslateDirective, HelpIconComponent, LineChartModule, ArtemisTranslatePipe], }) export class CourseExercisePerformanceComponent implements OnInit, OnChanges, OnDestroy { + private translateService = inject(TranslateService); + @Input() exercisePerformance: ExercisePerformance[] = []; yourScoreLabel: string; @@ -41,7 +47,7 @@ export class CourseExercisePerformanceComponent implements OnInit, OnChanges, On protected readonly YOUR_GRAPH_COLOR = YOUR_GRAPH_COLOR; protected readonly AVERAGE_GRAPH_COLOR = AVERAGE_GRAPH_COLOR; - constructor(private translateService: TranslateService) { + constructor() { this.translateServiceSubscription = this.translateService.onLangChange.subscribe(() => { this.setupChart(); }); @@ -55,7 +61,7 @@ export class CourseExercisePerformanceComponent implements OnInit, OnChanges, On this.translateServiceSubscription.unsubscribe(); } - ngOnChanges(): void { + ngOnChanges() { this.setupChart(); } diff --git a/src/main/webapp/app/overview/course-exams/course-exams.component.ts b/src/main/webapp/app/overview/course-exams/course-exams.component.ts index 33e28167ea69..4b59e30d337f 100644 --- a/src/main/webapp/app/overview/course-exams/course-exams.component.ts +++ b/src/main/webapp/app/overview/course-exams/course-exams.component.ts @@ -1,6 +1,6 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { Course } from 'app/entities/course.model'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { Subscription } from 'rxjs'; import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; @@ -13,6 +13,9 @@ import { AccordionGroups, CollapseState, SidebarCardElement, SidebarData } from import { CourseOverviewService } from '../course-overview.service'; import { cloneDeep } from 'lodash-es'; import { lastValueFrom } from 'rxjs'; +import { NgClass } from '@angular/common'; +import { SidebarComponent } from 'app/shared/sidebar/sidebar.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; const DEFAULT_UNIT_GROUPS: AccordionGroups = { real: { entityData: [] }, @@ -33,8 +36,16 @@ const DEFAULT_SHOW_ALWAYS: CollapseState = { selector: 'jhi-course-exams', templateUrl: './course-exams.component.html', styleUrls: ['./course-exams.component.scss'], + imports: [NgClass, SidebarComponent, RouterOutlet, TranslateDirective], }) export class CourseExamsComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private courseStorageService = inject(CourseStorageService); + private serverDateService = inject(ArtemisServerDateService); + private examParticipationService = inject(ExamParticipationService); + private courseOverviewService = inject(CourseOverviewService); + private router = inject(Router); + courseId: number; public course?: Course; private parentParamSubscription?: Subscription; @@ -64,15 +75,6 @@ export class CourseExamsComponent implements OnInit, OnDestroy { readonly DEFAULT_COLLAPSE_STATE = DEFAULT_COLLAPSE_STATE; protected readonly DEFAULT_SHOW_ALWAYS = DEFAULT_SHOW_ALWAYS; - constructor( - private route: ActivatedRoute, - private courseStorageService: CourseStorageService, - private serverDateService: ArtemisServerDateService, - private examParticipationService: ExamParticipationService, - private courseOverviewService: CourseOverviewService, - private router: Router, - ) {} - /** * subscribe to changes in the course and fetch course by the path parameter */ diff --git a/src/main/webapp/app/overview/course-exams/course-exams.module.ts b/src/main/webapp/app/overview/course-exams/course-exams.module.ts index 3bbf8e98043b..69d09384d273 100644 --- a/src/main/webapp/app/overview/course-exams/course-exams.module.ts +++ b/src/main/webapp/app/overview/course-exams/course-exams.module.ts @@ -7,8 +7,7 @@ import { ArtemisSharedComponentModule } from 'app/shared/components/shared-compo import { ArtemisSidebarModule } from 'app/shared/sidebar/sidebar.module'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedCommonModule, ArtemisSharedComponentModule, ArtemisExamSharedModule, ArtemisSidebarModule], - declarations: [CourseExamsComponent], + imports: [ArtemisSharedModule, ArtemisSharedCommonModule, ArtemisSharedComponentModule, ArtemisExamSharedModule, ArtemisSidebarModule, CourseExamsComponent], exports: [CourseExamsComponent], }) export class CourseExamsModule {} diff --git a/src/main/webapp/app/overview/course-exams/course-exams.route.ts b/src/main/webapp/app/overview/course-exams/course-exams.route.ts index 2e2a7264c14a..fc8482570ee0 100644 --- a/src/main/webapp/app/overview/course-exams/course-exams.route.ts +++ b/src/main/webapp/app/overview/course-exams/course-exams.route.ts @@ -1,12 +1,11 @@ import { Routes } from '@angular/router'; import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { CourseExamsComponent } from 'app/overview/course-exams/course-exams.component'; export const routes: Routes = [ { path: '', - component: CourseExamsComponent, + loadComponent: () => import('app/overview/course-exams/course-exams.component').then((m) => m.CourseExamsComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.exams', diff --git a/src/main/webapp/app/overview/course-exercises/course-exercise-row.component.ts b/src/main/webapp/app/overview/course-exercises/course-exercise-row.component.ts index d61d2da89cb5..ced1b1b19bd5 100644 --- a/src/main/webapp/app/overview/course-exercises/course-exercise-row.component.ts +++ b/src/main/webapp/app/overview/course-exercises/course-exercise-row.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { Component, HostBinding, Input, OnChanges, OnDestroy, OnInit, inject } from '@angular/core'; import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; import { ParticipationWebsocketService } from 'app/overview/participation-websocket.service'; import dayjs from 'dayjs/esm'; @@ -11,13 +11,44 @@ import { Exercise, ExerciseType, IncludedInOverallScore, getIcon, getIconTooltip import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { getExerciseDueDate } from 'app/exercises/shared/exercise/exercise.utils'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { SubmissionResultStatusComponent } from '../submission-result-status.component'; +import { ExerciseDetailsStudentActionsComponent } from '../exercise-details/exercise-details-student-actions.component'; +import { OrionFilterDirective } from 'app/shared/orion/orion-filter.directive'; +import { NgClass } from '@angular/common'; +import { ExerciseCategoriesComponent } from 'app/shared/exercise-categories/exercise-categories.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; @Component({ selector: 'jhi-course-exercise-row', templateUrl: './course-exercise-row.component.html', styleUrls: ['./course-exercise-row.scss'], + imports: [ + RouterLink, + FaIconComponent, + NgbTooltip, + SubmissionResultStatusComponent, + ExerciseDetailsStudentActionsComponent, + OrionFilterDirective, + NgClass, + ExerciseCategoriesComponent, + TranslateDirective, + ArtemisDatePipe, + ArtemisTranslatePipe, + ArtemisTimeAgoPipe, + ], }) export class CourseExerciseRowComponent implements OnInit, OnDestroy, OnChanges { + private accountService = inject(AccountService); + private participationService = inject(ParticipationService); + private exerciseService = inject(ExerciseService); + private participationWebsocketService = inject(ParticipationWebsocketService); + readonly IncludedInOverallScore = IncludedInOverallScore; readonly dayjs = dayjs; @HostBinding('class') classes = 'exercise-row'; @@ -40,13 +71,6 @@ export class CourseExerciseRowComponent implements OnInit, OnDestroy, OnChanges participationUpdateListener: Subscription; - constructor( - private accountService: AccountService, - private participationService: ParticipationService, - private exerciseService: ExerciseService, - private participationWebsocketService: ParticipationWebsocketService, - ) {} - ngOnInit() { if (this.exercise?.studentParticipations?.length) { this.gradedStudentParticipation = this.participationService.getSpecificStudentParticipation(this.exercise.studentParticipations, false); @@ -67,7 +91,7 @@ export class CourseExerciseRowComponent implements OnInit, OnDestroy, OnChanges this.routerLink = ['/courses', this.course.id!.toString(), 'exercises', this.exercise.id!.toString()]; } - ngOnChanges(): void { + ngOnChanges() { const cachedParticipations = this.participationWebsocketService.getParticipationsForExercise(this.exercise.id!); if (cachedParticipations?.length) { this.exercise.studentParticipations = cachedParticipations; diff --git a/src/main/webapp/app/overview/course-exercises/course-exercise-row.module.ts b/src/main/webapp/app/overview/course-exercises/course-exercise-row.module.ts index 50b444611e85..a7c9cf14a21a 100644 --- a/src/main/webapp/app/overview/course-exercises/course-exercise-row.module.ts +++ b/src/main/webapp/app/overview/course-exercises/course-exercise-row.module.ts @@ -21,8 +21,8 @@ import { ExerciseCategoriesModule } from 'app/shared/exercise-categories/exercis GradingKeyOverviewModule, SubmissionResultStatusModule, ExerciseCategoriesModule, + CourseExerciseRowComponent, ], - declarations: [CourseExerciseRowComponent], exports: [CourseExerciseRowComponent], }) export class ArtemisCourseExerciseRowModule {} diff --git a/src/main/webapp/app/overview/course-exercises/course-exercises.component.ts b/src/main/webapp/app/overview/course-exercises/course-exercises.component.ts index 5f0adeed54b5..abd416b192c6 100644 --- a/src/main/webapp/app/overview/course-exercises/course-exercises.component.ts +++ b/src/main/webapp/app/overview/course-exercises/course-exercises.component.ts @@ -1,6 +1,6 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { Course } from 'app/entities/course.model'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { Subscription } from 'rxjs'; import { GuidedTourService } from 'app/guided-tour/guided-tour.service'; import { courseExerciseOverviewTour } from 'app/guided-tour/tours/course-exercise-overview-tour'; @@ -10,6 +10,9 @@ import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { AccordionGroups, CollapseState, SidebarCardElement, SidebarData, SidebarItemShowAlways } from 'app/types/sidebar'; import { CourseOverviewService } from '../course-overview.service'; import { LtiService } from 'app/shared/service/lti.service'; +import { NgClass, NgStyle } from '@angular/common'; +import { SidebarComponent } from 'app/shared/sidebar/sidebar.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; const DEFAULT_UNIT_GROUPS: AccordionGroups = { future: { entityData: [] }, @@ -39,8 +42,17 @@ const DEFAULT_SHOW_ALWAYS: SidebarItemShowAlways = { selector: 'jhi-course-exercises', templateUrl: './course-exercises.component.html', styleUrls: ['../course-overview.scss'], + imports: [NgClass, SidebarComponent, NgStyle, RouterOutlet, TranslateDirective], }) export class CourseExercisesComponent implements OnInit, OnDestroy { + private courseStorageService = inject(CourseStorageService); + private route = inject(ActivatedRoute); + private guidedTourService = inject(GuidedTourService); + private programmingSubmissionService = inject(ProgrammingSubmissionService); + private router = inject(Router); + private courseOverviewService = inject(CourseOverviewService); + private ltiService = inject(LtiService); + private parentParamSubscription: Subscription; private courseUpdatesSubscription: Subscription; private ltiSubscription: Subscription; @@ -60,16 +72,6 @@ export class CourseExercisesComponent implements OnInit, OnDestroy { protected readonly DEFAULT_COLLAPSE_STATE = DEFAULT_COLLAPSE_STATE; protected readonly DEFAULT_SHOW_ALWAYS = DEFAULT_SHOW_ALWAYS; - constructor( - private courseStorageService: CourseStorageService, - private route: ActivatedRoute, - private guidedTourService: GuidedTourService, - private programmingSubmissionService: ProgrammingSubmissionService, - private router: Router, - private courseOverviewService: CourseOverviewService, - private ltiService: LtiService, - ) {} - ngOnInit() { this.isCollapsed = this.courseOverviewService.getSidebarCollapseStateFromStorage('exercise'); this.parentParamSubscription = this.route.parent!.params.subscribe((params) => { diff --git a/src/main/webapp/app/overview/course-faq/course-faq-accordion-component.ts b/src/main/webapp/app/overview/course-faq/course-faq-accordion-component.ts index 881a07f2d784..28233b9b60d1 100644 --- a/src/main/webapp/app/overview/course-faq/course-faq-accordion-component.ts +++ b/src/main/webapp/app/overview/course-faq/course-faq-accordion-component.ts @@ -1,5 +1,4 @@ import { Component, OnDestroy, input } from '@angular/core'; -import { TranslateDirective } from 'app/shared/language/translate.directive'; import { Faq } from 'app/entities/faq.model'; import { CustomExerciseCategoryBadgeComponent } from 'app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component'; import { Subject } from 'rxjs/internal/Subject'; @@ -9,9 +8,7 @@ import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; selector: 'jhi-course-faq-accordion', templateUrl: './course-faq-accordion.component.html', styleUrl: './course-faq-accordion.component.scss', - standalone: true, - - imports: [TranslateDirective, CustomExerciseCategoryBadgeComponent, ArtemisMarkdownModule], + imports: [CustomExerciseCategoryBadgeComponent, ArtemisMarkdownModule], }) export class CourseFaqAccordionComponent implements OnDestroy { private ngUnsubscribe = new Subject(); diff --git a/src/main/webapp/app/overview/course-faq/course-faq.component.ts b/src/main/webapp/app/overview/course-faq/course-faq.component.ts index 8138013ad4fa..0be571a70de4 100644 --- a/src/main/webapp/app/overview/course-faq/course-faq.component.ts +++ b/src/main/webapp/app/overview/course-faq/course-faq.component.ts @@ -19,14 +19,22 @@ import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.co import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; import { SortService } from 'app/shared/service/sort.service'; import { Renderer2 } from '@angular/core'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'jhi-course-faq', templateUrl: './course-faq.component.html', styleUrls: ['../course-overview.scss', 'course-faq.component.scss'], encapsulation: ViewEncapsulation.None, - standalone: true, - imports: [ArtemisSharedComponentModule, ArtemisSharedModule, CourseFaqAccordionComponent, CustomExerciseCategoryBadgeComponent, SearchFilterComponent, ArtemisMarkdownModule], + imports: [ + ArtemisSharedComponentModule, + ArtemisSharedModule, + CourseFaqAccordionComponent, + CustomExerciseCategoryBadgeComponent, + SearchFilterComponent, + ArtemisMarkdownModule, + NgbModule, + ], }) export class CourseFaqComponent implements OnInit, OnDestroy { faqElements = viewChildren('faqElement'); diff --git a/src/main/webapp/app/overview/course-lectures/attachment-unit/attachment-unit.component.ts b/src/main/webapp/app/overview/course-lectures/attachment-unit/attachment-unit.component.ts index ecc6c9f39f85..cde836380a81 100644 --- a/src/main/webapp/app/overview/course-lectures/attachment-unit/attachment-unit.component.ts +++ b/src/main/webapp/app/overview/course-lectures/attachment-unit/attachment-unit.component.ts @@ -22,7 +22,6 @@ import { FileService } from 'app/shared/http/file.service'; @Component({ selector: 'jhi-attachment-unit', - standalone: true, imports: [LectureUnitComponent, ArtemisSharedCommonModule], templateUrl: './attachment-unit.component.html', }) diff --git a/src/main/webapp/app/overview/course-lectures/course-lecture-details.component.ts b/src/main/webapp/app/overview/course-lectures/course-lecture-details.component.ts index 14cc75d10ef7..301803ba2bea 100644 --- a/src/main/webapp/app/overview/course-lectures/course-lecture-details.component.ts +++ b/src/main/webapp/app/overview/course-lectures/course-lecture-details.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { HttpErrorResponse } from '@angular/common/http'; import { downloadStream } from 'app/shared/util/download.util'; @@ -16,10 +16,22 @@ import { faSpinner } from '@fortawesome/free-solid-svg-icons'; import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; import { isCommunicationEnabled, isMessagingEnabled } from 'app/entities/course.model'; import { AbstractScienceComponent } from 'app/shared/science/science.component'; -import { ScienceService } from 'app/shared/science/science.service'; import { ScienceEventType } from 'app/shared/science/science.model'; import { Subscription } from 'rxjs'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass, UpperCasePipe } from '@angular/common'; +import { ExerciseUnitComponent } from './exercise-unit/exercise-unit.component'; +import { AttachmentUnitComponent } from './attachment-unit/attachment-unit.component'; +import { VideoUnitComponent } from './video-unit/video-unit.component'; +import { TextUnitComponent } from './text-unit/text-unit.component'; +import { OnlineUnitComponent } from './online-unit/online-unit.component'; +import { CompetenciesPopoverComponent } from 'app/course/competencies/competencies-popover/competencies-popover.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { DiscussionSectionComponent } from '../discussion-section/discussion-section.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; export interface LectureUnitCompletionEvent { lectureUnit: LectureUnit; @@ -30,8 +42,32 @@ export interface LectureUnitCompletionEvent { selector: 'jhi-course-lecture-details', templateUrl: './course-lecture-details.component.html', styleUrls: ['../course-overview.scss', './course-lectures.scss'], + imports: [ + TranslateDirective, + NgClass, + ExerciseUnitComponent, + AttachmentUnitComponent, + VideoUnitComponent, + TextUnitComponent, + OnlineUnitComponent, + CompetenciesPopoverComponent, + FaIconComponent, + DiscussionSectionComponent, + UpperCasePipe, + ArtemisDatePipe, + ArtemisTranslatePipe, + HtmlForMarkdownPipe, + ], }) export class CourseLectureDetailsComponent extends AbstractScienceComponent implements OnInit, OnDestroy { + private alertService = inject(AlertService); + private lectureService = inject(LectureService); + private lectureUnitService = inject(LectureUnitService); + private activatedRoute = inject(ActivatedRoute); + private fileService = inject(FileService); + private router = inject(Router); + private profileService = inject(ProfileService); + lectureId?: number; courseId?: number; isLoading = false; @@ -53,17 +89,8 @@ export class CourseLectureDetailsComponent extends AbstractScienceComponent impl // Icons faSpinner = faSpinner; - constructor( - private alertService: AlertService, - private lectureService: LectureService, - private lectureUnitService: LectureUnitService, - private activatedRoute: ActivatedRoute, - private fileService: FileService, - private router: Router, - scienceService: ScienceService, - private profileService: ProfileService, - ) { - super(scienceService, ScienceEventType.LECTURE__OPEN); + constructor() { + super(ScienceEventType.LECTURE__OPEN); } ngOnInit(): void { diff --git a/src/main/webapp/app/overview/course-lectures/course-lecture-details.module.ts b/src/main/webapp/app/overview/course-lectures/course-lecture-details.module.ts index 315de44054b0..000ffd522b6c 100644 --- a/src/main/webapp/app/overview/course-lectures/course-lecture-details.module.ts +++ b/src/main/webapp/app/overview/course-lectures/course-lecture-details.module.ts @@ -1,8 +1,9 @@ +import { CourseLectureDetailsComponent } from 'app/overview/course-lectures/course-lecture-details.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ArtemisSharedPipesModule } from 'app/shared/pipes/shared-pipes.module'; import { ArtemisLectureUnitsModule } from 'app/overview/course-lectures/lecture-units.module'; -import { CourseLectureDetailsComponent } from 'app/overview/course-lectures/course-lecture-details.component'; + import { NgModule } from '@angular/core'; import { ArtemisCompetenciesModule } from 'app/course/competencies/competency.module'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; @@ -18,7 +19,7 @@ import { DiscussionSectionComponent } from 'app/overview/discussion-section/disc const routes: Routes = [ { path: '', - component: CourseLectureDetailsComponent, + loadComponent: () => import('app/overview/course-lectures/course-lecture-details.component').then((m) => m.CourseLectureDetailsComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.lectures', @@ -40,8 +41,8 @@ const routes: Routes = [ OnlineUnitComponent, AttachmentUnitComponent, DiscussionSectionComponent, + CourseLectureDetailsComponent, ], - declarations: [CourseLectureDetailsComponent], exports: [CourseLectureDetailsComponent], }) export class ArtemisCourseLectureDetailsModule {} diff --git a/src/main/webapp/app/overview/course-lectures/course-lecture-row.component.ts b/src/main/webapp/app/overview/course-lectures/course-lecture-row.component.ts index 9cdb6b4e09f2..10e584b6a94a 100644 --- a/src/main/webapp/app/overview/course-lectures/course-lecture-row.component.ts +++ b/src/main/webapp/app/overview/course-lectures/course-lecture-row.component.ts @@ -3,11 +3,20 @@ import dayjs from 'dayjs/esm'; import { Course } from 'app/entities/course.model'; import { Lecture } from 'app/entities/lecture.model'; import { faChalkboardTeacher } from '@fortawesome/free-solid-svg-icons'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { NgClass } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; @Component({ selector: 'jhi-course-lecture-row', templateUrl: './course-lecture-row.component.html', styleUrls: ['../course-exercises/course-exercise-row.scss'], + imports: [RouterLink, FaIconComponent, NgbTooltip, NgClass, TranslateDirective, ArtemisDatePipe, ArtemisTranslatePipe, ArtemisTimeAgoPipe], }) export class CourseLectureRowComponent { @HostBinding('class') classes = 'exercise-row'; @@ -17,8 +26,6 @@ export class CourseLectureRowComponent { // Icons faChalkboardTeacher = faChalkboardTeacher; - constructor() {} - getUrgentClass(date?: dayjs.Dayjs) { if (!date) { return ''; diff --git a/src/main/webapp/app/overview/course-lectures/course-lectures.component.ts b/src/main/webapp/app/overview/course-lectures/course-lectures.component.ts index 8e12e392d125..3457662017e8 100644 --- a/src/main/webapp/app/overview/course-lectures/course-lectures.component.ts +++ b/src/main/webapp/app/overview/course-lectures/course-lectures.component.ts @@ -1,11 +1,14 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { Course } from 'app/entities/course.model'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { Subscription } from 'rxjs'; import { Lecture } from 'app/entities/lecture.model'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { AccordionGroups, CollapseState, SidebarCardElement, SidebarData, SidebarItemShowAlways } from 'app/types/sidebar'; import { CourseOverviewService } from '../course-overview.service'; +import { NgClass } from '@angular/common'; +import { SidebarComponent } from 'app/shared/sidebar/sidebar.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; const DEFAULT_UNIT_GROUPS: AccordionGroups = { future: { entityData: [] }, @@ -35,8 +38,14 @@ const DEFAULT_SHOW_ALWAYS: SidebarItemShowAlways = { selector: 'jhi-course-lectures', templateUrl: './course-lectures.component.html', styleUrls: ['../course-overview.scss'], + imports: [NgClass, SidebarComponent, RouterOutlet, TranslateDirective], }) export class CourseLecturesComponent implements OnInit, OnDestroy { + private courseStorageService = inject(CourseStorageService); + private route = inject(ActivatedRoute); + private router = inject(Router); + private courseOverviewService = inject(CourseOverviewService); + private parentParamSubscription: Subscription; private courseUpdatesSubscription: Subscription; course?: Course; @@ -51,13 +60,6 @@ export class CourseLecturesComponent implements OnInit, OnDestroy { readonly DEFAULT_COLLAPSE_STATE = DEFAULT_COLLAPSE_STATE; protected readonly DEFAULT_SHOW_ALWAYS = DEFAULT_SHOW_ALWAYS; - constructor( - private courseStorageService: CourseStorageService, - private route: ActivatedRoute, - private router: Router, - private courseOverviewService: CourseOverviewService, - ) {} - ngOnInit() { this.isCollapsed = this.courseOverviewService.getSidebarCollapseStateFromStorage('lecture'); this.parentParamSubscription = this.route.parent!.params.subscribe((params) => { diff --git a/src/main/webapp/app/overview/course-lectures/exercise-unit/exercise-unit.component.ts b/src/main/webapp/app/overview/course-lectures/exercise-unit/exercise-unit.component.ts index b415b0e95ac4..ecdf4679c624 100644 --- a/src/main/webapp/app/overview/course-lectures/exercise-unit/exercise-unit.component.ts +++ b/src/main/webapp/app/overview/course-lectures/exercise-unit/exercise-unit.component.ts @@ -1,12 +1,14 @@ import { Component, HostBinding, Input, ViewEncapsulation } from '@angular/core'; import { Course } from 'app/entities/course.model'; import { ExerciseUnit } from 'app/entities/lecture-unit/exerciseUnit.model'; +import { CourseExerciseRowComponent } from '../../course-exercises/course-exercise-row.component'; @Component({ selector: 'jhi-exercise-unit', templateUrl: './exercise-unit.component.html', encapsulation: ViewEncapsulation.None, styleUrls: ['./exercise-unit.component.scss'], + imports: [CourseExerciseRowComponent], }) export class ExerciseUnitComponent { @HostBinding('className') componentClass: string; diff --git a/src/main/webapp/app/overview/course-lectures/lecture-unit/lecture-unit.component.ts b/src/main/webapp/app/overview/course-lectures/lecture-unit/lecture-unit.component.ts index b4cf868d5d5a..583c0bd11dcc 100644 --- a/src/main/webapp/app/overview/course-lectures/lecture-unit/lecture-unit.component.ts +++ b/src/main/webapp/app/overview/course-lectures/lecture-unit/lecture-unit.component.ts @@ -7,7 +7,6 @@ import { LectureUnit } from 'app/entities/lecture-unit/lectureUnit.model'; @Component({ selector: 'jhi-lecture-unit-card', - standalone: true, imports: [FontAwesomeModule, ArtemisSharedCommonModule, NgbCollapseModule], templateUrl: './lecture-unit.component.html', styleUrl: './lecture-unit.component.scss', diff --git a/src/main/webapp/app/overview/course-lectures/lecture-unit/lecture-unit.directive.ts b/src/main/webapp/app/overview/course-lectures/lecture-unit/lecture-unit.directive.ts index 51fe2cf35726..190645cc5b14 100644 --- a/src/main/webapp/app/overview/course-lectures/lecture-unit/lecture-unit.directive.ts +++ b/src/main/webapp/app/overview/course-lectures/lecture-unit/lecture-unit.directive.ts @@ -1,24 +1,21 @@ import { AbstractScienceComponent } from 'app/shared/science/science.component'; -import { ScienceService } from 'app/shared/science/science.service'; import { ScienceEventType } from 'app/shared/science/science.model'; import { LectureUnit } from 'app/entities/lecture-unit/lectureUnit.model'; import { effect, input, output } from '@angular/core'; import { Directive } from '@angular/core'; import { LectureUnitCompletionEvent } from 'app/overview/course-lectures/course-lecture-details.component'; -@Directive({ - standalone: true, -}) +@Directive() export class LectureUnitDirective extends AbstractScienceComponent { - readonly lectureUnit = input.required(); + lectureUnit = input.required(); readonly onCompletion = output(); readonly onCollapse = output(); - readonly isPresentationMode = input(false); + isPresentationMode = input(false); - constructor(scienceService: ScienceService) { - super(scienceService, ScienceEventType.LECTURE__OPEN_UNIT); + constructor() { + super(ScienceEventType.LECTURE__OPEN_UNIT); effect(() => { this.setResourceId(this.lectureUnit().id!); diff --git a/src/main/webapp/app/overview/course-lectures/lecture-units.module.ts b/src/main/webapp/app/overview/course-lectures/lecture-units.module.ts index 5ef001e15d5f..e8a662b7af81 100644 --- a/src/main/webapp/app/overview/course-lectures/lecture-units.module.ts +++ b/src/main/webapp/app/overview/course-lectures/lecture-units.module.ts @@ -7,8 +7,7 @@ import { ExerciseUnitComponent } from 'app/overview/course-lectures/exercise-uni import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisSharedPipesModule, ArtemisCourseExerciseRowModule, ArtemisMarkdownModule], - declarations: [ExerciseUnitComponent], + imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisSharedPipesModule, ArtemisCourseExerciseRowModule, ArtemisMarkdownModule, ExerciseUnitComponent], exports: [ExerciseUnitComponent], }) export class ArtemisLectureUnitsModule {} diff --git a/src/main/webapp/app/overview/course-lectures/online-unit/online-unit.component.ts b/src/main/webapp/app/overview/course-lectures/online-unit/online-unit.component.ts index 9bc979d903a3..f2880956b71b 100644 --- a/src/main/webapp/app/overview/course-lectures/online-unit/online-unit.component.ts +++ b/src/main/webapp/app/overview/course-lectures/online-unit/online-unit.component.ts @@ -6,7 +6,6 @@ import { faUpRightFromSquare } from '@fortawesome/free-solid-svg-icons'; @Component({ selector: 'jhi-online-unit', - standalone: true, imports: [LectureUnitComponent], templateUrl: './online-unit.component.html', }) diff --git a/src/main/webapp/app/overview/course-lectures/text-unit/text-unit.component.ts b/src/main/webapp/app/overview/course-lectures/text-unit/text-unit.component.ts index 7563ccc34e1b..f9f19721182e 100644 --- a/src/main/webapp/app/overview/course-lectures/text-unit/text-unit.component.ts +++ b/src/main/webapp/app/overview/course-lectures/text-unit/text-unit.component.ts @@ -8,7 +8,6 @@ import { LectureUnitDirective } from 'app/overview/course-lectures/lecture-unit/ @Component({ selector: 'jhi-text-unit', - standalone: true, imports: [LectureUnitComponent], templateUrl: './text-unit.component.html', }) diff --git a/src/main/webapp/app/overview/course-lectures/video-unit/video-unit.component.ts b/src/main/webapp/app/overview/course-lectures/video-unit/video-unit.component.ts index 7cbf2829048b..9284448e63d9 100644 --- a/src/main/webapp/app/overview/course-lectures/video-unit/video-unit.component.ts +++ b/src/main/webapp/app/overview/course-lectures/video-unit/video-unit.component.ts @@ -8,7 +8,6 @@ import { LectureUnitComponent } from 'app/overview/course-lectures/lecture-unit/ @Component({ selector: 'jhi-video-unit', - standalone: true, imports: [ArtemisSharedPipesModule, LectureUnitComponent], templateUrl: './video-unit.component.html', }) diff --git a/src/main/webapp/app/overview/course-overview.component.ts b/src/main/webapp/app/overview/course-overview.component.ts index 8e09014c4fb1..3056f4857a61 100644 --- a/src/main/webapp/app/overview/course-overview.component.ts +++ b/src/main/webapp/app/overview/course-overview.component.ts @@ -14,7 +14,7 @@ import { ViewContainerRef, inject, } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { IconDefinition, faChalkboardUser, @@ -39,7 +39,7 @@ import { faTimes, faWrench, } from '@fortawesome/free-solid-svg-icons'; -import { NgbDropdown, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbDropdown, NgbDropdownButtonItem, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle, NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { AlertService, AlertType } from 'app/core/util/alert.service'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { CourseAccessStorageService } from 'app/course/course-access-storage.service'; @@ -57,7 +57,7 @@ import { ArtemisServerDateService } from 'app/shared/server-date.service'; import { BarControlConfiguration, BarControlConfigurationProvider } from 'app/shared/tab-bar/tab-bar'; import dayjs from 'dayjs/esm'; import { Observable, Subject, Subscription, catchError, firstValueFrom, map, of, takeUntil, throwError } from 'rxjs'; -import { facSidebar } from '../../content/icons/icons'; +import { facSidebar } from 'app/icons/icons'; import { CourseManagementService } from '../course/manage/course-management.service'; import { CourseExercisesComponent } from './course-exercises/course-exercises.component'; import { CourseLecturesComponent } from './course-lectures/course-lectures.component'; @@ -69,6 +69,13 @@ import { sortCourses } from 'app/shared/util/course.util'; import { CourseUnenrollmentModalComponent } from './course-unenrollment-modal.component'; import { LtiService } from 'app/shared/service/lti.service'; import { CourseSidebarService } from 'app/overview/course-sidebar.service'; +import { NgClass, NgStyle, NgTemplateOutlet, SlicePipe } from '@angular/common'; +import { MatSidenav, MatSidenavContainer, MatSidenavContent } from '@angular/material/sidenav'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from '../shared/language/translate.directive'; +import { SecuredImageComponent } from '../shared/image/secured-image.component'; +import { OrionFilterDirective } from '../shared/orion/orion-filter.directive'; +import { FeatureToggleHideDirective } from '../shared/feature-toggle/feature-toggle-hide.directive'; interface CourseActionItem { title: string; @@ -95,8 +102,48 @@ interface SidebarItem { templateUrl: './course-overview.component.html', styleUrls: ['course-overview.scss', 'course-overview.component.scss'], providers: [MetisConversationService], + imports: [ + NgClass, + MatSidenavContainer, + MatSidenav, + NgbDropdown, + NgbDropdownToggle, + NgTemplateOutlet, + NgbDropdownMenu, + NgbDropdownButtonItem, + NgbDropdownItem, + FaIconComponent, + TranslateDirective, + NgbTooltip, + MatSidenavContent, + NgStyle, + RouterLink, + RouterOutlet, + SecuredImageComponent, + OrionFilterDirective, + RouterLinkActive, + FeatureToggleHideDirective, + SlicePipe, + ], }) export class CourseOverviewComponent implements OnInit, OnDestroy, AfterViewInit { + private courseService = inject(CourseManagementService); + private courseExerciseService = inject(CourseExerciseService); + private courseStorageService = inject(CourseStorageService); + private route = inject(ActivatedRoute); + private teamService = inject(TeamService); + private jhiWebsocketService = inject(JhiWebsocketService); + private serverDateService = inject(ArtemisServerDateService); + private alertService = inject(AlertService); + private changeDetectorRef = inject(ChangeDetectorRef); + private metisConversationService = inject(MetisConversationService); + private router = inject(Router); + private courseAccessStorageService = inject(CourseAccessStorageService); + private profileService = inject(ProfileService); + private modalService = inject(NgbModal); + private examParticipationService = inject(ExamParticipationService); + private ltiService = inject(LtiService); + private ngUnsubscribe = new Subject(); private closeSidebarEventSubscription: Subscription; private openSidebarEventSubscription: Subscription; @@ -118,7 +165,7 @@ export class CourseOverviewComponent implements OnInit, OnDestroy, AfterViewInit isProduction = true; isTestServer = false; pageTitle: string; - hasSidebar: boolean = false; + hasSidebar = false; sidebarItems: SidebarItem[]; courseActionItems: CourseActionItem[]; isNotManagementView: boolean; @@ -126,7 +173,7 @@ export class CourseOverviewComponent implements OnInit, OnDestroy, AfterViewInit isNavbarCollapsed = false; isSidebarCollapsed = false; profileSubscription?: Subscription; - showRefreshButton: boolean = false; + showRefreshButton = false; isExamStarted = false; private examStartedSubscription: Subscription; readonly MIN_DISPLAYED_COURSES: number = 6; @@ -189,25 +236,6 @@ export class CourseOverviewComponent implements OnInit, OnDestroy, AfterViewInit private courseSidebarService: CourseSidebarService = inject(CourseSidebarService); - constructor( - private courseService: CourseManagementService, - private courseExerciseService: CourseExerciseService, - private courseStorageService: CourseStorageService, - private route: ActivatedRoute, - private teamService: TeamService, - private jhiWebsocketService: JhiWebsocketService, - private serverDateService: ArtemisServerDateService, - private alertService: AlertService, - private changeDetectorRef: ChangeDetectorRef, - private metisConversationService: MetisConversationService, - private router: Router, - private courseAccessStorageService: CourseAccessStorageService, - private profileService: ProfileService, - private modalService: NgbModal, - private examParticipationService: ExamParticipationService, - private ltiService: LtiService, - ) {} - async ngOnInit() { this.openSidebarEventSubscription = this.courseSidebarService.openSidebar$.subscribe(() => { this.isSidebarCollapsed = true; diff --git a/src/main/webapp/app/overview/course-overview.service.ts b/src/main/webapp/app/overview/course-overview.service.ts index 6e13440242bb..8408654338cf 100644 --- a/src/main/webapp/app/overview/course-overview.service.ts +++ b/src/main/webapp/app/overview/course-overview.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Exercise, getIcon } from 'app/entities/exercise.model'; import { Lecture } from 'app/entities/lecture.model'; @@ -71,11 +71,9 @@ const DEFAULT_CHANNEL_GROUPS: AccordionGroups = { providedIn: 'root', }) export class CourseOverviewService { - constructor( - private participationService: ParticipationService, - private translate: TranslateService, - private conversationService: ConversationService, - ) {} + private participationService = inject(ParticipationService); + private translate = inject(TranslateService); + private conversationService = inject(ConversationService); readonly faBullhorn = faBullhorn; readonly faHashtag = faHashtag; diff --git a/src/main/webapp/app/overview/course-registration/course-prerequisites-button/course-prerequisites-button.component.ts b/src/main/webapp/app/overview/course-registration/course-prerequisites-button/course-prerequisites-button.component.ts index 765c849561f8..ca92ba3a701e 100644 --- a/src/main/webapp/app/overview/course-registration/course-prerequisites-button/course-prerequisites-button.component.ts +++ b/src/main/webapp/app/overview/course-registration/course-prerequisites-button/course-prerequisites-button.component.ts @@ -1,16 +1,18 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { Course } from 'app/entities/course.model'; import { CoursePrerequisitesModalComponent } from 'app/overview/course-registration/course-registration-prerequisites-modal/course-prerequisites-modal.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-course-prerequisites-button', templateUrl: './course-prerequisites-button.component.html', + imports: [TranslateDirective], }) export class CoursePrerequisitesButtonComponent { - @Input() course: Course; + private modalService = inject(NgbModal); - constructor(private modalService: NgbModal) {} + @Input() course: Course; /** * Opens a modal with the prerequisites for the course diff --git a/src/main/webapp/app/overview/course-registration/course-prerequisites-button/course-prerequisites-button.module.ts b/src/main/webapp/app/overview/course-registration/course-prerequisites-button/course-prerequisites-button.module.ts index fea08dd0fe9c..ab1126a79968 100644 --- a/src/main/webapp/app/overview/course-registration/course-prerequisites-button/course-prerequisites-button.module.ts +++ b/src/main/webapp/app/overview/course-registration/course-prerequisites-button/course-prerequisites-button.module.ts @@ -6,8 +6,7 @@ import { CoursePrerequisitesButtonComponent } from 'app/overview/course-registra import { CoursePrerequisitesModalModule } from 'app/overview/course-registration/course-registration-prerequisites-modal/course-prerequisites-modal.module'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule, CoursePrerequisitesModalModule], - declarations: [CoursePrerequisitesButtonComponent], + imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule, CoursePrerequisitesModalModule, CoursePrerequisitesButtonComponent], exports: [CoursePrerequisitesButtonComponent], }) export class CoursePrerequisitesButtonModule {} diff --git a/src/main/webapp/app/overview/course-registration/course-registration-button/course-registration-button.component.ts b/src/main/webapp/app/overview/course-registration/course-registration-button/course-registration-button.component.ts index 0dacd75e250c..3b38f79e22d6 100644 --- a/src/main/webapp/app/overview/course-registration/course-registration-button/course-registration-button.component.ts +++ b/src/main/webapp/app/overview/course-registration/course-registration-button/course-registration-button.component.ts @@ -1,29 +1,30 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { AccountService } from 'app/core/auth/account.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { Course } from 'app/entities/course.model'; import { matchesRegexFully } from 'app/utils/regex.util'; import { AlertService } from 'app/core/util/alert.service'; +import { ConfirmAutofocusButtonComponent } from 'app/shared/components/confirm-autofocus-button.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-course-registration-button', templateUrl: './course-registration-button.component.html', + imports: [ConfirmAutofocusButtonComponent, TranslateDirective], }) export class CourseRegistrationButtonComponent implements OnInit { + private accountService = inject(AccountService); + private courseService = inject(CourseManagementService); + private profileService = inject(ProfileService); + private alertService = inject(AlertService); + @Input() course: Course; @Output() onRegistration = new EventEmitter(); userIsAllowedToRegister = false; loading = false; - constructor( - private accountService: AccountService, - private courseService: CourseManagementService, - private profileService: ProfileService, - private alertService: AlertService, - ) {} - loadUserIsAllowedToRegister() { this.loading = true; this.accountService.identity().then((user) => { diff --git a/src/main/webapp/app/overview/course-registration/course-registration-button/course-registration-button.module.ts b/src/main/webapp/app/overview/course-registration/course-registration-button/course-registration-button.module.ts index a5248566e71d..11535e93dc5d 100644 --- a/src/main/webapp/app/overview/course-registration/course-registration-button/course-registration-button.module.ts +++ b/src/main/webapp/app/overview/course-registration/course-registration-button/course-registration-button.module.ts @@ -5,8 +5,7 @@ import { ArtemisCompetenciesModule } from 'app/course/competencies/competency.mo import { CourseRegistrationButtonComponent } from 'app/overview/course-registration/course-registration-button/course-registration-button.component'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule], - declarations: [CourseRegistrationButtonComponent], + imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule, CourseRegistrationButtonComponent], exports: [CourseRegistrationButtonComponent], }) export class CourseRegistrationButtonModule {} diff --git a/src/main/webapp/app/overview/course-registration/course-registration-detail/course-registration-detail.component.ts b/src/main/webapp/app/overview/course-registration/course-registration-detail/course-registration-detail.component.ts index 09c3086a8ea5..9abb842aac51 100644 --- a/src/main/webapp/app/overview/course-registration/course-registration-detail/course-registration-detail.component.ts +++ b/src/main/webapp/app/overview/course-registration/course-registration-detail/course-registration-detail.component.ts @@ -1,28 +1,30 @@ import { HttpErrorResponse } from '@angular/common/http'; -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { AccountService } from 'app/core/auth/account.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { Course } from 'app/entities/course.model'; import { Observable, catchError, map, of, throwError } from 'rxjs'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CoursePrerequisitesButtonComponent } from '../course-prerequisites-button/course-prerequisites-button.component'; +import { CourseRegistrationButtonComponent } from '../course-registration-button/course-registration-button.component'; @Component({ selector: 'jhi-course-registration-detail-selector', templateUrl: './course-registration-detail.component.html', + imports: [TranslateDirective, CoursePrerequisitesButtonComponent, CourseRegistrationButtonComponent], }) export class CourseRegistrationDetailComponent implements OnInit, OnDestroy { + private accountService = inject(AccountService); + private courseService = inject(CourseManagementService); + private route = inject(ActivatedRoute); + private router = inject(Router); + loading = false; courseId: number; course: Course | null = null; private paramSubscription: any; - constructor( - private accountService: AccountService, - private courseService: CourseManagementService, - private route: ActivatedRoute, - private router: Router, - ) {} - ngOnInit(): void { this.loading = true; this.paramSubscription = this.route.parent!.params.subscribe((params) => { diff --git a/src/main/webapp/app/overview/course-registration/course-registration-detail/course-registration-detail.module.ts b/src/main/webapp/app/overview/course-registration/course-registration-detail/course-registration-detail.module.ts index 973b58af98ae..940212a8299b 100644 --- a/src/main/webapp/app/overview/course-registration/course-registration-detail/course-registration-detail.module.ts +++ b/src/main/webapp/app/overview/course-registration/course-registration-detail/course-registration-detail.module.ts @@ -1,17 +1,19 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { CourseRegistrationDetailComponent } from 'app/overview/course-registration/course-registration-detail/course-registration-detail.component'; import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; -import { CourseRegistrationDetailComponent } from 'app/overview/course-registration/course-registration-detail/course-registration-detail.component'; + import { CoursePrerequisitesButtonModule } from 'app/overview/course-registration/course-prerequisites-button/course-prerequisites-button.module'; import { CourseRegistrationButtonModule } from 'app/overview/course-registration/course-registration-button/course-registration-button.module'; const routes: Routes = [ { path: '', - component: CourseRegistrationDetailComponent, + loadComponent: () => + import('app/overview/course-registration/course-registration-detail/course-registration-detail.component').then((m) => m.CourseRegistrationDetailComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.studentDashboard.enroll.title', @@ -21,7 +23,13 @@ const routes: Routes = [ ]; @NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, RouterModule.forChild(routes), CoursePrerequisitesButtonModule, CourseRegistrationButtonModule], - declarations: [CourseRegistrationDetailComponent], + imports: [ + ArtemisSharedModule, + ArtemisSharedComponentModule, + RouterModule.forChild(routes), + CoursePrerequisitesButtonModule, + CourseRegistrationButtonModule, + CourseRegistrationDetailComponent, + ], }) export class CourseRegistrationDetailModule {} diff --git a/src/main/webapp/app/overview/course-registration/course-registration-prerequisites-modal/course-prerequisites-modal.component.ts b/src/main/webapp/app/overview/course-registration/course-registration-prerequisites-modal/course-prerequisites-modal.component.ts index 9e2bd74f2adf..c0f84b71a3c5 100644 --- a/src/main/webapp/app/overview/course-registration/course-registration-prerequisites-modal/course-prerequisites-modal.component.ts +++ b/src/main/webapp/app/overview/course-registration/course-registration-prerequisites-modal/course-prerequisites-modal.component.ts @@ -1,28 +1,28 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { AlertService } from 'app/core/util/alert.service'; import { finalize } from 'rxjs/operators'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { PrerequisiteService } from 'app/course/competencies/prerequisite.service'; import { Prerequisite } from 'app/entities/prerequisite.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CompetencyCardComponent } from 'app/course/competencies/competency-card/competency-card.component'; @Component({ selector: 'jhi-course-prerequisites-modal', templateUrl: './course-prerequisites-modal.component.html', - styles: [], + imports: [TranslateDirective, CompetencyCardComponent], }) export class CoursePrerequisitesModalComponent implements OnInit { + private alertService = inject(AlertService); + private activeModal = inject(NgbActiveModal); + private prerequisiteService = inject(PrerequisiteService); + @Input() courseId: number; isLoading = false; prerequisites: Prerequisite[] = []; - constructor( - private alertService: AlertService, - private activeModal: NgbActiveModal, - private prerequisiteService: PrerequisiteService, - ) {} - ngOnInit(): void { if (this.courseId) { this.loadData(); diff --git a/src/main/webapp/app/overview/course-registration/course-registration-prerequisites-modal/course-prerequisites-modal.module.ts b/src/main/webapp/app/overview/course-registration/course-registration-prerequisites-modal/course-prerequisites-modal.module.ts index 8e8b0a852bb8..f3122ec53333 100644 --- a/src/main/webapp/app/overview/course-registration/course-registration-prerequisites-modal/course-prerequisites-modal.module.ts +++ b/src/main/webapp/app/overview/course-registration/course-registration-prerequisites-modal/course-prerequisites-modal.module.ts @@ -5,7 +5,6 @@ import { CoursePrerequisitesModalComponent } from 'app/overview/course-registrat import { ArtemisCompetenciesModule } from 'app/course/competencies/competency.module'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule], - declarations: [CoursePrerequisitesModalComponent], + imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule, CoursePrerequisitesModalComponent], }) export class CoursePrerequisitesModalModule {} diff --git a/src/main/webapp/app/overview/course-registration/course-registration.component.ts b/src/main/webapp/app/overview/course-registration/course-registration.component.ts index 36483cae58a1..9bdbbf73f539 100644 --- a/src/main/webapp/app/overview/course-registration/course-registration.component.ts +++ b/src/main/webapp/app/overview/course-registration/course-registration.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { AccountService } from 'app/core/auth/account.service'; import { Course } from 'app/entities/course.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; @@ -7,12 +7,36 @@ import { ASC, DESC, SORT } from 'app/shared/constants/pagination.constants'; import { ActivatedRoute, Router } from '@angular/router'; import { combineLatest } from 'rxjs'; import { SortService } from 'app/shared/service/sort.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { CoursePrerequisitesButtonComponent } from './course-prerequisites-button/course-prerequisites-button.component'; +import { CourseRegistrationButtonComponent } from './course-registration-button/course-registration-button.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-course-registration-selector', templateUrl: './course-registration.component.html', + imports: [ + TranslateDirective, + FormsModule, + SortDirective, + SortByDirective, + FaIconComponent, + CoursePrerequisitesButtonComponent, + CourseRegistrationButtonComponent, + ArtemisTranslatePipe, + ], }) export class CourseRegistrationComponent implements OnInit { + private accountService = inject(AccountService); + private courseService = inject(CourseManagementService); + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private sortService = inject(SortService); + coursesToSelect: Course[] = []; loading = false; predicate!: string; @@ -23,14 +47,6 @@ export class CourseRegistrationComponent implements OnInit { faCheckCircle = faCheckCircle; faSort = faSort; - constructor( - private accountService: AccountService, - private courseService: CourseManagementService, - private activatedRoute: ActivatedRoute, - private router: Router, - private sortService: SortService, - ) {} - ngOnInit(): void { this.handleNavigation(); } diff --git a/src/main/webapp/app/overview/course-registration/course-registration.module.ts b/src/main/webapp/app/overview/course-registration/course-registration.module.ts index 1fea470fdf8e..8acb12cd1b3d 100644 --- a/src/main/webapp/app/overview/course-registration/course-registration.module.ts +++ b/src/main/webapp/app/overview/course-registration/course-registration.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { CourseRegistrationComponent } from 'app/overview/course-registration/course-registration.component'; + import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { ArtemisSharedModule } from 'app/shared/shared.module'; @@ -12,7 +13,7 @@ import { CoursePrerequisitesButtonModule } from 'app/overview/course-registratio const routes: Routes = [ { path: '', - component: CourseRegistrationComponent, + loadComponent: () => import('app/overview/course-registration/course-registration.component').then((m) => m.CourseRegistrationComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.studentDashboard.enroll.title', @@ -29,7 +30,7 @@ const routes: Routes = [ ArtemisCompetenciesModule, CourseRegistrationButtonModule, CoursePrerequisitesButtonModule, + CourseRegistrationComponent, ], - declarations: [CourseRegistrationComponent], }) export class CourseRegistrationModule {} diff --git a/src/main/webapp/app/overview/course-sidebar.service.ts b/src/main/webapp/app/overview/course-sidebar.service.ts index 642de68c5e33..189a3fb1c380 100644 --- a/src/main/webapp/app/overview/course-sidebar.service.ts +++ b/src/main/webapp/app/overview/course-sidebar.service.ts @@ -8,8 +8,6 @@ export class CourseSidebarService { public openSidebar$: EventEmitter = new EventEmitter(); public toggleSidebar$: EventEmitter = new EventEmitter(); - constructor() {} - public closeSidebar(): void { this.closeSidebar$.emit(); } diff --git a/src/main/webapp/app/overview/course-statistics/course-statistics.component.ts b/src/main/webapp/app/overview/course-statistics/course-statistics.component.ts index 27ba9ed43ef3..345c8d95b97d 100644 --- a/src/main/webapp/app/overview/course-statistics/course-statistics.component.ts +++ b/src/main/webapp/app/overview/course-statistics/course-statistics.component.ts @@ -1,8 +1,8 @@ -import { AfterViewInit, Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { AfterViewInit, Component, OnDestroy, OnInit, TemplateRef, ViewChild, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { faClipboard, faFilter, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { TranslateService } from '@ngx-translate/core'; -import { Color, ScaleType } from '@swimlane/ngx-charts'; +import { BarChartModule, Color, PieChartModule, ScaleType } from '@swimlane/ngx-charts'; import { CourseScores } from 'app/course/course-scores/course-scores'; import { ScoresStorageService } from 'app/course/course-scores/scores-storage.service'; import { ParticipationResultDTO } from 'app/course/manage/course-for-dashboard-dto'; @@ -25,6 +25,13 @@ import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; import dayjs from 'dayjs/esm'; import { sortBy } from 'lodash-es'; import { Subject, Subscription } from 'rxjs'; +import { NgbDropdown, NgbDropdownMenu, NgbDropdownToggle, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { ExerciseScoresChartComponent } from '../visualizations/exercise-scores-chart/exercise-scores-chart.component'; +import { KeyValuePipe } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; const QUIZ_EXERCISE_COLOR = '#17a2b8'; const PROGRAMMING_EXERCISE_COLOR = '#fd7e14'; @@ -92,8 +99,31 @@ enum ChartBarTitle { selector: 'jhi-course-statistics', templateUrl: './course-statistics.component.html', styleUrls: ['../course-overview.scss'], + imports: [ + NgbDropdown, + NgbDropdownToggle, + FaIconComponent, + TranslateDirective, + NgbDropdownMenu, + DocumentationButtonComponent, + PieChartModule, + NgbTooltip, + RouterLink, + ExerciseScoresChartComponent, + BarChartModule, + KeyValuePipe, + ArtemisTranslatePipe, + ], }) export class CourseStatisticsComponent implements OnInit, OnDestroy, AfterViewInit, BarControlConfigurationProvider { + private courseStorageService = inject(CourseStorageService); + private scoresStorageService = inject(ScoresStorageService); + private translateService = inject(TranslateService); + private route = inject(ActivatedRoute); + private gradingSystemService = inject(GradingSystemService); + private navigationUtilService = inject(ArtemisNavigationUtilService); + categoryFilter = inject(ChartCategoryFilter); + readonly documentationType: DocumentationType = 'Statistics'; courseId: number; @@ -213,16 +243,6 @@ export class CourseStatisticsComponent implements OnInit, OnDestroy, AfterViewIn subject: new Subject>(), }; - constructor( - private courseStorageService: CourseStorageService, - private scoresStorageService: ScoresStorageService, - private translateService: TranslateService, - private route: ActivatedRoute, - private gradingSystemService: GradingSystemService, - private navigationUtilService: ArtemisNavigationUtilService, - public categoryFilter: ChartCategoryFilter, - ) {} - ngOnInit() { // Note: due to lazy loading and router outlet, we use parent 2x here this.paramSubscription = this.route.parent?.parent?.params.subscribe((params) => { diff --git a/src/main/webapp/app/overview/course-statistics/course-statistics.module.ts b/src/main/webapp/app/overview/course-statistics/course-statistics.module.ts index 5ef402281eb4..dc8ef5f4e22c 100644 --- a/src/main/webapp/app/overview/course-statistics/course-statistics.module.ts +++ b/src/main/webapp/app/overview/course-statistics/course-statistics.module.ts @@ -1,19 +1,20 @@ import { NgModule } from '@angular/core'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; import { CourseStatisticsComponent } from 'app/overview/course-statistics/course-statistics.component'; +import { ArtemisSharedModule } from 'app/shared/shared.module'; + import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { RouterModule, Routes } from '@angular/router'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { ArtemisExerciseScoresChartModule } from 'app/overview/visualizations/exercise-scores-chart.module'; import { BarChartModule, PieChartModule } from '@swimlane/ngx-charts'; -import { GradingKeyOverviewComponent } from 'app/grading-system/grading-key-overview/grading-key-overview.component'; + import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; const routes: Routes = [ { path: '', - component: CourseStatisticsComponent, + loadComponent: () => import('app/overview/course-statistics/course-statistics.component').then((m) => m.CourseStatisticsComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.statistics', @@ -22,7 +23,7 @@ const routes: Routes = [ }, { path: 'grading-key', - component: GradingKeyOverviewComponent, + loadComponent: () => import('app/grading-system/grading-key-overview/grading-key-overview.component').then((m) => m.GradingKeyOverviewComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.gradingSystem.title', @@ -40,7 +41,7 @@ const routes: Routes = [ BarChartModule, PieChartModule, ArtemisSharedComponentModule, + CourseStatisticsComponent, ], - declarations: [CourseStatisticsComponent], }) export class CourseStatisticsModule {} diff --git a/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-group-card/course-tutorial-group-card.component.ts b/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-group-card/course-tutorial-group-card.component.ts index 53bbf5cd4d99..d23f745134ca 100644 --- a/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-group-card/course-tutorial-group-card.component.ts +++ b/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-group-card/course-tutorial-group-card.component.ts @@ -2,6 +2,10 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { faPersonChalkboard } from '@fortawesome/free-solid-svg-icons'; import { Course, isMessagingEnabled } from 'app/entities/course.model'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-course-tutorial-group-card', @@ -9,6 +13,7 @@ import { Course, isMessagingEnabled } from 'app/entities/course.model'; styleUrls: ['./course-tutorial-group-card.component.scss'], host: { class: 'card tutorial-group-card' }, changeDetection: ChangeDetectionStrategy.OnPush, + imports: [RouterLink, FaIconComponent, TranslateDirective, ArtemisDatePipe], }) export class CourseTutorialGroupCardComponent { @Input() diff --git a/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-groups.component.ts b/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-groups.component.ts index ed90dd068fb1..be130f8e7f8c 100644 --- a/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-groups.component.ts +++ b/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-groups.component.ts @@ -1,8 +1,8 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { Subject, finalize } from 'rxjs'; import { Course } from 'app/entities/course.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterOutlet } from '@angular/router'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { TutorialGroupsService } from 'app/course/tutorial-groups/services/tutorial-groups.service'; import { map, takeUntil } from 'rxjs/operators'; @@ -15,6 +15,9 @@ import { TutorialGroupsConfiguration } from 'app/entities/tutorial-group/tutoria import { AccordionGroups, CollapseState, SidebarCardElement, SidebarData, SidebarItemShowAlways, TutorialGroupCategory } from 'app/types/sidebar'; import { CourseOverviewService } from '../course-overview.service'; import { cloneDeep } from 'lodash-es'; +import { NgClass } from '@angular/common'; +import { SidebarComponent } from 'app/shared/sidebar/sidebar.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; const TUTORIAL_UNIT_GROUPS: AccordionGroups = { registered: { entityData: [] }, @@ -38,8 +41,18 @@ const DEFAULT_SHOW_ALWAYS: SidebarItemShowAlways = { selector: 'jhi-course-tutorial-groups', templateUrl: './course-tutorial-groups.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [NgClass, SidebarComponent, RouterOutlet, TranslateDirective], }) export class CourseTutorialGroupsComponent implements OnInit, OnDestroy { + private router = inject(Router); + private courseStorageService = inject(CourseStorageService); + private courseManagementService = inject(CourseManagementService); + private tutorialGroupService = inject(TutorialGroupsService); + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + private cdr = inject(ChangeDetectorRef); + private courseOverviewService = inject(CourseOverviewService); + ngUnsubscribe = new Subject(); tutorialGroups: TutorialGroup[] = []; @@ -48,9 +61,9 @@ export class CourseTutorialGroupsComponent implements OnInit, OnDestroy { configuration?: TutorialGroupsConfiguration; isLoading = false; tutorialGroupFreeDays: TutorialGroupFreePeriod[] = []; - isCollapsed: boolean = false; + isCollapsed = false; - tutorialGroupSelected: boolean = true; + tutorialGroupSelected = true; sidebarData: SidebarData; sortedTutorialGroups: TutorialGroup[] = []; accordionTutorialGroupsGroups: AccordionGroups = TUTORIAL_UNIT_GROUPS; @@ -58,17 +71,6 @@ export class CourseTutorialGroupsComponent implements OnInit, OnDestroy { protected readonly DEFAULT_SHOW_ALWAYS = DEFAULT_SHOW_ALWAYS; sidebarTutorialGroups: SidebarCardElement[] = []; - constructor( - private router: Router, - private courseStorageService: CourseStorageService, - private courseManagementService: CourseManagementService, - private tutorialGroupService: TutorialGroupsService, - private route: ActivatedRoute, - private alertService: AlertService, - private cdr: ChangeDetectorRef, - private courseOverviewService: CourseOverviewService, - ) {} - get registeredTutorialGroups() { if (this.course?.isAtLeastTutor) { return this.tutorialGroups.filter((tutorialGroup) => tutorialGroup.isUserTutor); diff --git a/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-groups.module.ts b/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-groups.module.ts index af87e1b67370..687a0fff4a2e 100644 --- a/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-groups.module.ts +++ b/src/main/webapp/app/overview/course-tutorial-groups/course-tutorial-groups.module.ts @@ -4,10 +4,8 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { CourseTutorialGroupCardComponent } from './course-tutorial-group-card/course-tutorial-group-card.component'; import { ArtemisTutorialGroupsSharedModule } from 'app/course/tutorial-groups/shared/tutorial-groups-shared.module'; import { ArtemisSidebarModule } from 'app/shared/sidebar/sidebar.module'; -import { ArtemisAppRoutingModule } from 'app/app-routing.module'; @NgModule({ - imports: [ArtemisAppRoutingModule, ArtemisSharedModule, ArtemisTutorialGroupsSharedModule, ArtemisSidebarModule], - declarations: [CourseTutorialGroupsComponent, CourseTutorialGroupCardComponent], + imports: [ArtemisSharedModule, ArtemisTutorialGroupsSharedModule, ArtemisSidebarModule, CourseTutorialGroupsComponent, CourseTutorialGroupCardComponent], }) export class CourseTutorialGroupsModule {} diff --git a/src/main/webapp/app/overview/course-unenrollment-modal.component.ts b/src/main/webapp/app/overview/course-unenrollment-modal.component.ts index 0a27a334da31..d295ac24e948 100644 --- a/src/main/webapp/app/overview/course-unenrollment-modal.component.ts +++ b/src/main/webapp/app/overview/course-unenrollment-modal.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { Router } from '@angular/router'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { faXmark } from '@fortawesome/free-solid-svg-icons'; @@ -6,26 +6,29 @@ import dayjs from 'dayjs/esm'; import { Course } from 'app/entities/course.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { AlertService } from 'app/core/util/alert.service'; -import { AbstractControl, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; +import { AbstractControl, FormControl, FormGroup, FormsModule, ReactiveFormsModule, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; +import { TranslateDirective } from '../shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from '../shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-course-unenrollment-modal', templateUrl: './course-unenrollment-modal.component.html', + imports: [TranslateDirective, FormsModule, ReactiveFormsModule, FaIconComponent, ArtemisDatePipe, ArtemisTranslatePipe], }) export class CourseUnenrollmentModalComponent implements OnInit { + private activeModal = inject(NgbActiveModal); + private courseService = inject(CourseManagementService); + private alertService = inject(AlertService); + private router = inject(Router); + public course: Course; confirmationForm: FormGroup; // Icons faXmark = faXmark; - constructor( - private activeModal: NgbActiveModal, - private courseService: CourseManagementService, - private alertService: AlertService, - private router: Router, - ) {} - ngOnInit(): void { this.confirmationForm = new FormGroup({ confirmationInput: new FormControl('', Validators.compose([Validators.required, this.confirmationInputValidator()])), diff --git a/src/main/webapp/app/overview/courses-routing.module.ts b/src/main/webapp/app/overview/courses-routing.module.ts index 68e90e184205..0669891b2db4 100644 --- a/src/main/webapp/app/overview/courses-routing.module.ts +++ b/src/main/webapp/app/overview/courses-routing.module.ts @@ -1,17 +1,12 @@ import { RouterModule, Routes } from '@angular/router'; -import { CoursesComponent } from 'app/overview/courses.component'; + import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { CourseLecturesComponent } from 'app/overview/course-lectures/course-lectures.component'; + import { NgModule } from '@angular/core'; import { Authority } from 'app/shared/constants/authority.constants'; -import { CourseExercisesComponent } from 'app/overview/course-exercises/course-exercises.component'; -import { CourseOverviewComponent } from './course-overview.component'; -import { CourseExamsComponent } from './course-exams/course-exams.component'; -import { CourseTutorialGroupsComponent } from './course-tutorial-groups/course-tutorial-groups.component'; -import { CourseTutorialGroupDetailComponent } from './tutorial-group-details/course-tutorial-group-detail/course-tutorial-group-detail.component'; -import { ExamParticipationComponent } from 'app/exam/participate/exam-participation.component'; + import { PendingChangesGuard } from 'app/shared/guard/pending-changes.guard'; -import { CourseArchiveComponent } from './course-archive/course-archive.component'; + import { CourseOverviewGuard } from 'app/overview/course-overview-guard'; export enum CourseOverviewRoutePath { @@ -32,7 +27,7 @@ export enum CourseOverviewRoutePath { const routes: Routes = [ { path: '', - component: CoursesComponent, + loadComponent: () => import('app/overview/courses.component').then((m) => m.CoursesComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.title', @@ -45,7 +40,7 @@ const routes: Routes = [ }, { path: CourseOverviewRoutePath.ARCHIVE, - component: CourseArchiveComponent, + loadComponent: () => import('./course-archive/course-archive.component').then((m) => m.CourseArchiveComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.archive', @@ -61,7 +56,7 @@ const routes: Routes = [ }, { path: ':courseId', - component: CourseOverviewComponent, + loadComponent: () => import('./course-overview.component').then((m) => m.CourseOverviewComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.course', @@ -70,7 +65,7 @@ const routes: Routes = [ children: [ { path: CourseOverviewRoutePath.EXERCISES, - component: CourseExercisesComponent, + loadComponent: () => import('app/overview/course-exercises/course-exercises.component').then((m) => m.CourseExercisesComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.exercises', @@ -144,7 +139,7 @@ const routes: Routes = [ { path: CourseOverviewRoutePath.LECTURES, - component: CourseLecturesComponent, + loadComponent: () => import('app/overview/course-lectures/course-lectures.component').then((m) => m.CourseLecturesComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.lectures', @@ -226,7 +221,7 @@ const routes: Routes = [ }, { path: CourseOverviewRoutePath.TUTORIAL_GROUPS, - component: CourseTutorialGroupsComponent, + loadComponent: () => import('./course-tutorial-groups/course-tutorial-groups.component').then((m) => m.CourseTutorialGroupsComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.tutorialGroups', @@ -237,7 +232,10 @@ const routes: Routes = [ children: [ { path: ':tutorialGroupId', - component: CourseTutorialGroupDetailComponent, + loadComponent: () => + import('./tutorial-group-details/course-tutorial-group-detail/course-tutorial-group-detail.component').then( + (m) => m.CourseTutorialGroupDetailComponent, + ), data: { authorities: [Authority.USER], pageTitle: 'overview.tutorialGroups', @@ -251,7 +249,7 @@ const routes: Routes = [ }, { path: CourseOverviewRoutePath.EXAMS, - component: CourseExamsComponent, + loadComponent: () => import('./course-exams/course-exams.component').then((m) => m.CourseExamsComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.exams', @@ -262,7 +260,7 @@ const routes: Routes = [ children: [ { path: ':examId', - component: ExamParticipationComponent, + loadComponent: () => import('app/exam/participate/exam-participation.component').then((m) => m.ExamParticipationComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.exams', diff --git a/src/main/webapp/app/overview/courses.component.ts b/src/main/webapp/app/overview/courses.component.ts index 2ad16cae7fcd..bf145a496679 100644 --- a/src/main/webapp/app/overview/courses.component.ts +++ b/src/main/webapp/app/overview/courses.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { CoursesForDashboardDTO } from 'app/course/manage/courses-for-dashboard-dto'; import { Course } from 'app/entities/course.model'; import { CourseManagementService } from '../course/manage/course-management.service'; @@ -9,18 +9,44 @@ import { TeamService } from 'app/exercises/shared/team/team.service'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import dayjs from 'dayjs/esm'; import { Exam } from 'app/entities/exam/exam.model'; -import { Router } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; import { faArrowDownAZ, faArrowUpAZ, faDoorOpen, faPenAlt } from '@fortawesome/free-solid-svg-icons'; import { CourseAccessStorageService } from 'app/course/course-access-storage.service'; import { CourseForDashboardDTO } from 'app/course/manage/course-for-dashboard-dto'; import { sortCourses } from 'app/shared/util/course.util'; +import { TranslateDirective } from '../shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { SearchFilterComponent } from '../shared/search-filter/search-filter.component'; +import { NgTemplateOutlet } from '@angular/common'; +import { CourseCardComponent } from './course-card.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from '../shared/pipes/artemis-translate.pipe'; +import { SearchFilterPipe } from 'app/shared/pipes/search-filter.pipe'; @Component({ selector: 'jhi-overview', templateUrl: './courses.component.html', styleUrls: ['./courses.component.scss'], + imports: [ + TranslateDirective, + FaIconComponent, + SearchFilterComponent, + RouterLink, + NgTemplateOutlet, + CourseCardComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + SearchFilterPipe, + ], }) export class CoursesComponent implements OnInit, OnDestroy { + private courseService = inject(CourseManagementService); + private guidedTourService = inject(GuidedTourService); + private teamService = inject(TeamService); + private jhiWebsocketService = inject(JhiWebsocketService); + private router = inject(Router); + private courseAccessStorageService = inject(CourseAccessStorageService); + protected readonly faPenAlt = faPenAlt; protected readonly faArrowDownAZ = faArrowDownAZ; protected readonly faArrowUpAZ = faArrowUpAZ; @@ -41,15 +67,6 @@ export class CoursesComponent implements OnInit, OnDestroy { coursesLoaded = false; isSortAscending = true; - constructor( - private courseService: CourseManagementService, - private guidedTourService: GuidedTourService, - private teamService: TeamService, - private jhiWebsocketService: JhiWebsocketService, - private router: Router, - private courseAccessStorageService: CourseAccessStorageService, - ) {} - async ngOnInit() { this.loadAndFilterCourses(); (await this.teamService.teamAssignmentUpdates).subscribe(); diff --git a/src/main/webapp/app/overview/courses.module.ts b/src/main/webapp/app/overview/courses.module.ts index 9d6693a17fdc..687ea33da9f8 100644 --- a/src/main/webapp/app/overview/courses.module.ts +++ b/src/main/webapp/app/overview/courses.module.ts @@ -3,7 +3,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSidePanelModule } from 'app/shared/side-panel/side-panel.module'; import { CourseLectureRowComponent } from 'app/overview/course-lectures/course-lecture-row.component'; import { OrionModule } from 'app/shared/orion/orion.module'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { CourseCardComponent } from 'app/overview/course-card.component'; import { CourseOverviewComponent } from 'app/overview/course-overview.component'; import { ArtemisHeaderExercisePageWithDetailsModule } from 'app/exercises/shared/exercise-headers/exercise-headers.module'; @@ -32,14 +32,18 @@ import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.co ArtemisHeaderExercisePageWithDetailsModule, OrionModule, ArtemisComplaintsModule, - FeatureToggleModule, RatingModule, NgxChartsModule, PieChartModule, ArtemisSidebarModule, CourseCardComponent, SearchFilterComponent, + CoursesComponent, + CourseOverviewComponent, + CourseExercisesComponent, + CourseLecturesComponent, + CourseLectureRowComponent, + CourseUnenrollmentModalComponent, ], - declarations: [CoursesComponent, CourseOverviewComponent, CourseExercisesComponent, CourseLecturesComponent, CourseLectureRowComponent, CourseUnenrollmentModalComponent], }) export class ArtemisCoursesModule {} diff --git a/src/main/webapp/app/overview/discussion-section/discussion-section.component.ts b/src/main/webapp/app/overview/discussion-section/discussion-section.component.ts index d272247a86cf..1429c2b9fbc5 100644 --- a/src/main/webapp/app/overview/discussion-section/discussion-section.component.ts +++ b/src/main/webapp/app/overview/discussion-section/discussion-section.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, ElementRef, OnDestroy, QueryList, ViewChild, ViewChildren, effect, input } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, OnDestroy, QueryList, ViewChild, ViewChildren, effect, inject, input } from '@angular/core'; import interact from 'interactjs'; import { Exercise } from 'app/entities/exercise.model'; import { Lecture } from 'app/entities/lecture.model'; @@ -11,23 +11,27 @@ import { PostCreateEditModalComponent } from 'app/shared/metis/posting-create-ed import { HttpResponse } from '@angular/common/http'; import { faArrowLeft, faChevronLeft, faChevronRight, faGripLinesVertical, faLongArrowRight } from '@fortawesome/free-solid-svg-icons'; import { CourseDiscussionDirective } from 'app/shared/metis/course-discussion.directive'; -import { FormBuilder } from '@angular/forms'; +import { FormBuilder, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Channel, ChannelDTO } from 'app/entities/metis/conversation/channel.model'; import { ChannelService } from 'app/shared/metis/conversations/channel.service'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisPlagiarismCasesSharedModule } from 'app/course/plagiarism-cases/shared/plagiarism-cases-shared.module'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; -import { InfiniteScrollModule } from 'ngx-infinite-scroll'; +import { InfiniteScrollDirective } from 'ngx-infinite-scroll'; @Component({ selector: 'jhi-discussion-section', templateUrl: './discussion-section.component.html', styleUrls: ['./discussion-section.component.scss'], - imports: [FontAwesomeModule, ArtemisSharedModule, ArtemisPlagiarismCasesSharedModule, InfiniteScrollModule], - standalone: true, + imports: [FontAwesomeModule, ArtemisSharedModule, ArtemisPlagiarismCasesSharedModule, InfiniteScrollDirective, FormsModule, ReactiveFormsModule], providers: [MetisService], }) export class DiscussionSectionComponent extends CourseDiscussionDirective implements AfterViewInit, OnDestroy { + private channelService = inject(ChannelService); + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private formBuilder = inject(FormBuilder); + exercise = input(); lecture = input(); @@ -59,14 +63,8 @@ export class DiscussionSectionComponent extends CourseDiscussionDirective implem faArrowLeft = faArrowLeft; faLongArrowRight = faLongArrowRight; - constructor( - protected metisService: MetisService, - private channelService: ChannelService, - private activatedRoute: ActivatedRoute, - private router: Router, - private formBuilder: FormBuilder, - ) { - super(metisService); + constructor() { + super(); effect(() => this.loadData(this.exercise(), this.lecture())); } diff --git a/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.html b/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.html index 05e6e4d9d53e..a6b096b431c8 100644 --- a/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.html +++ b/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.html @@ -25,12 +25,14 @@
    + + + + + + + + @if (plagiarismCaseInfo && plagiarismCaseInfo.verdict !== PlagiarismVerdict.NO_PLAGIARISM) { @if (!plagiarismCaseInfo?.createdByContinuousPlagiarismControl) { diff --git a/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts b/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts index 6b81fedc9ad0..06b82b93db7d 100644 --- a/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts +++ b/src/main/webapp/app/overview/exercise-details/course-exercise-details.component.ts @@ -1,6 +1,6 @@ -import { Component, ContentChild, OnDestroy, OnInit, TemplateRef } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { Subscription, combineLatest } from 'rxjs'; import { filter, skip } from 'rxjs/operators'; import { Result } from 'app/entities/result.model'; @@ -17,13 +17,11 @@ import { ExampleSolutionInfo, ExerciseDetailsType, ExerciseService } from 'app/e import { AssessmentType } from 'app/entities/assessment-type.model'; import { hasExerciseDueDatePassed } from 'app/exercises/shared/exercise/exercise.utils'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { GradingCriterion } from 'app/exercises/shared/structured-grading-criterion/grading-criterion.model'; import { AlertService } from 'app/core/util/alert.service'; import { TeamAssignmentPayload } from 'app/entities/team.model'; import { TeamService } from 'app/exercises/shared/team/team.service'; import { QuizExercise, QuizStatus } from 'app/entities/quiz/quiz-exercise.model'; import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.service'; -import { ExerciseCategory } from 'app/entities/exercise-category.model'; import { getFirstResultWithComplaintFromResults } from 'app/entities/submission.model'; import { ComplaintService } from 'app/complaints/complaint.service'; import { Complaint } from 'app/entities/complaint.model'; @@ -37,11 +35,30 @@ import { isCommunicationEnabled, isMessagingEnabled } from 'app/entities/course. import { ExerciseCacheService } from 'app/exercises/shared/exercise/exercise-cache.service'; import { IrisSettings } from 'app/entities/iris/settings/iris-settings.model'; import { AbstractScienceComponent } from 'app/shared/science/science.component'; -import { ScienceService } from 'app/shared/science/science.service'; import { ScienceEventType } from 'app/shared/science/science.model'; import { PROFILE_IRIS } from 'app/app.constants'; import { ChatServiceMode } from 'app/iris/iris-chat.service'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ExerciseDetailsStudentActionsComponent } from './exercise-details-student-actions.component'; +import { ExerciseHeadersInformationComponent } from 'app/exercises/shared/exercise-headers/exercise-headers-information/exercise-headers-information.component'; +import { ResultHistoryComponent } from '../result-history/result-history.component'; +import { ResultComponent } from 'app/exercises/shared/result/result.component'; +import { ProblemStatementComponent } from './problem-statement/problem-statement.component'; +import { ResetRepoButtonComponent } from 'app/shared/components/reset-repo-button/reset-repo-button.component'; +import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; +import { ProgrammingExerciseExampleSolutionRepoDownloadComponent } from 'app/exercises/programming/shared/actions/programming-exercise-example-solution-repo-download.component'; +import { ExerciseInfoComponent } from 'app/exercises/shared/exercise-info/exercise-info.component'; +import { ComplaintsStudentViewComponent } from 'app/complaints/complaints-for-students/complaints-student-view.component'; +import { RatingComponent } from 'app/exercises/shared/rating/rating.component'; +import { IrisExerciseChatbotButtonComponent } from 'app/iris/exercise-chatbot/exercise-chatbot-button.component'; +import { DiscussionSectionComponent } from '../discussion-section/discussion-section.component'; +import { LtiInitializerComponent } from './lti-initializer.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; interface InstructorActionItem { routerLink: string; @@ -53,8 +70,49 @@ interface InstructorActionItem { templateUrl: './course-exercise-details.component.html', styleUrls: ['../course-overview.scss', './course-exercise-details.component.scss'], providers: [ExerciseCacheService], + imports: [ + NgClass, + FaIconComponent, + NgbDropdown, + NgbDropdownToggle, + NgbDropdownMenu, + NgbDropdownItem, + RouterLink, + TranslateDirective, + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // ExtensionPointDirective, + ExerciseDetailsStudentActionsComponent, + ExerciseHeadersInformationComponent, + ResultHistoryComponent, + ResultComponent, + ProblemStatementComponent, + ResetRepoButtonComponent, + ModelingEditorComponent, + ProgrammingExerciseExampleSolutionRepoDownloadComponent, + NgbTooltip, + ExerciseInfoComponent, + ComplaintsStudentViewComponent, + RatingComponent, + IrisExerciseChatbotButtonComponent, + DiscussionSectionComponent, + LtiInitializerComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + ], }) export class CourseExerciseDetailsComponent extends AbstractScienceComponent implements OnInit, OnDestroy { + private exerciseService = inject(ExerciseService); + private participationWebsocketService = inject(ParticipationWebsocketService); + private participationService = inject(ParticipationService); + private route = inject(ActivatedRoute); + private profileService = inject(ProfileService); + private guidedTourService = inject(GuidedTourService); + private alertService = inject(AlertService); + private teamService = inject(TeamService); + private quizExerciseService = inject(QuizExerciseService); + private complaintService = inject(ComplaintService); + private artemisMarkdown = inject(ArtemisMarkdownService); + readonly AssessmentType = AssessmentType; readonly PlagiarismVerdict = PlagiarismVerdict; readonly QuizStatus = QuizStatus; @@ -75,12 +133,11 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp public exerciseId: number; public courseId: number; public exercise: Exercise; - public resultWithComplaint?: Result; - public latestRatedResult?: Result; - public complaint?: Complaint; - public showMoreResults = false; + resultWithComplaint?: Result; + latestRatedResult?: Result; + complaint?: Complaint; + showMoreResults = false; public sortedHistoryResults: Result[]; - public exerciseCategories: ExerciseCategory[]; private participationUpdateListener: Subscription; private teamAssignmentUpdateListener: Subscription; private submissionSubscription: Subscription; @@ -89,9 +146,7 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp practiceStudentParticipation?: StudentParticipation; isAfterAssessmentDueDate: boolean; allowComplaintsForAutomaticAssessments: boolean; - public gradingCriteria: GradingCriterion[]; baseResource: string; - isExamExercise: boolean; submissionPolicy?: SubmissionPolicy; exampleSolutionCollapsed: boolean; plagiarismCaseInfo?: PlagiarismCaseInfo; @@ -100,14 +155,15 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp profileSubscription?: Subscription; isProduction = true; isTestServer = false; - isGeneratingFeedback: boolean = false; instructorActionItems: InstructorActionItem[] = []; exerciseIcon: IconProp; exampleSolutionInfo?: ExampleSolutionInfo; // extension points, see shared/extension-point - @ContentChild('overrideStudentActions') overrideStudentActions: TemplateRef; + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // @ContentChild('overrideStudentActions') overrideStudentActions: TemplateRef; + // Icons faBook = faBook; faEye = faEye; @@ -117,21 +173,8 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp faAngleDown = faAngleDown; faAngleUp = faAngleUp; - constructor( - private exerciseService: ExerciseService, - private participationWebsocketService: ParticipationWebsocketService, - private participationService: ParticipationService, - private route: ActivatedRoute, - private profileService: ProfileService, - private guidedTourService: GuidedTourService, - private alertService: AlertService, - private teamService: TeamService, - private quizExerciseService: QuizExerciseService, - private complaintService: ComplaintService, - private artemisMarkdown: ArtemisMarkdownService, - scienceService: ScienceService, - ) { - super(scienceService, ScienceEventType.EXERCISE__OPEN); + constructor() { + super(ScienceEventType.EXERCISE__OPEN); } ngOnInit() { @@ -182,7 +225,6 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp this.filterUnfinishedResults(this.exercise.studentParticipations); this.mergeResultsAndSubmissionsForParticipations(); this.isAfterAssessmentDueDate = !this.exercise.assessmentDueDate || dayjs().isAfter(this.exercise.assessmentDueDate); - this.exerciseCategories = this.exercise.categories ?? []; this.allowComplaintsForAutomaticAssessments = false; this.plagiarismCaseInfo = newExerciseDetails.plagiarismCaseInfo; if (this.exercise.type === ExerciseType.PROGRAMMING) { @@ -295,7 +337,6 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp changedParticipation.id === this.gradedStudentParticipation?.id && (changedParticipation.results?.length || 0) > (this.gradedStudentParticipation?.results?.length || 0) ) { - this.isGeneratingFeedback = false; this.alertService.success('artemisApp.exercise.lateSubmissionResultReceived'); } if ( @@ -304,7 +345,6 @@ export class CourseExerciseDetailsComponent extends AbstractScienceComponent imp changedParticipation.results?.last()?.assessmentType === AssessmentType.AUTOMATIC_ATHENA && changedParticipation.results?.last()?.successful !== undefined ) { - this.isGeneratingFeedback = false; if (changedParticipation.results?.last()?.successful === true) { this.alertService.success('artemisApp.exercise.athenaFeedbackSuccessful'); } else { diff --git a/src/main/webapp/app/overview/exercise-details/course-exercise-details.module.ts b/src/main/webapp/app/overview/exercise-details/course-exercise-details.module.ts index ec6495463ecc..289c8e30b68e 100644 --- a/src/main/webapp/app/overview/exercise-details/course-exercise-details.module.ts +++ b/src/main/webapp/app/overview/exercise-details/course-exercise-details.module.ts @@ -1,3 +1,4 @@ +import { OrionCourseExerciseDetailsComponent } from 'app/orion/participation/orion-course-exercise-details.component'; import { CourseExerciseDetailsComponent } from 'app/overview/exercise-details/course-exercise-details.component'; import { ArtemisProgrammingExerciseInstructionsRenderModule } from 'app/exercises/programming/shared/instructions-render/programming-exercise-instructions-render.module'; import { RouterModule, Routes } from '@angular/router'; @@ -5,7 +6,7 @@ import { NgModule } from '@angular/core'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSidePanelModule } from 'app/shared/side-panel/side-panel.module'; import { OrionModule } from 'app/shared/orion/orion.module'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { ArtemisHeaderExercisePageWithDetailsModule } from 'app/exercises/shared/exercise-headers/exercise-headers.module'; import { ArtemisResultModule } from 'app/exercises/shared/result/result.module'; import { ArtemisComplaintsModule } from 'app/complaints/complaints.module'; @@ -16,8 +17,7 @@ import { ArtemisExerciseButtonsModule } from 'app/overview/exercise-details/exer import { ArtemisCourseExerciseRowModule } from 'app/overview/course-exercises/course-exercise-row.module'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { Authority } from 'app/shared/constants/authority.constants'; -import { OrionCourseExerciseDetailsComponent } from 'app/orion/participation/orion-course-exercise-details.component'; -import { isOrion } from 'app/shared/orion/orion'; + import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { ArtemisModelingEditorModule } from 'app/exercises/modeling/shared/modeling-editor.module'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; @@ -35,7 +35,7 @@ import { ExerciseHeadersInformationComponent } from 'app/exercises/shared/exerci const routes: Routes = [ { path: '', - component: !isOrion ? CourseExerciseDetailsComponent : OrionCourseExerciseDetailsComponent, + loadComponent: () => import('app/orion/participation/orion-course-exercise-details.component').then((m) => m.OrionCourseExerciseDetailsComponent), data: { authorities: [Authority.USER], pageTitle: 'overview.exercise', @@ -59,7 +59,6 @@ const standaloneComponents = [ExerciseHeadersInformationComponent]; ArtemisHeaderExercisePageWithDetailsModule, OrionModule, ArtemisComplaintsModule, - FeatureToggleModule, FontAwesomeModule, RatingModule, ArtemisProgrammingExerciseInstructionsRenderModule, @@ -73,8 +72,12 @@ const standaloneComponents = [ExerciseHeadersInformationComponent]; IrisModule, DiscussionSectionComponent, [...standaloneComponents], + CourseExerciseDetailsComponent, + OrionCourseExerciseDetailsComponent, + LtiInitializerComponent, + LtiInitializerModalComponent, + ProblemStatementComponent, ], - declarations: [CourseExerciseDetailsComponent, OrionCourseExerciseDetailsComponent, LtiInitializerComponent, LtiInitializerModalComponent, ProblemStatementComponent], exports: [CourseExerciseDetailsComponent, OrionCourseExerciseDetailsComponent, ProblemStatementComponent], }) export class CourseExerciseDetailsModule {} diff --git a/src/main/webapp/app/overview/exercise-details/exercise-buttons.module.ts b/src/main/webapp/app/overview/exercise-details/exercise-buttons.module.ts index 7b6aab3d5f7c..6bf6489f09ef 100644 --- a/src/main/webapp/app/overview/exercise-details/exercise-buttons.module.ts +++ b/src/main/webapp/app/overview/exercise-details/exercise-buttons.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { OrionModule } from 'app/shared/orion/orion.module'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { OrionExerciseDetailsStudentActionsComponent } from 'app/orion/participation/orion-exercise-details-student-actions.component'; import { ExerciseDetailsStudentActionsComponent } from 'app/overview/exercise-details/exercise-details-student-actions.component'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; @@ -9,8 +9,15 @@ import { ArtemisSharedPipesModule } from 'app/shared/pipes/shared-pipes.module'; import { RequestFeedbackButtonComponent } from 'app/overview/exercise-details/request-feedback-button/request-feedback-button.component'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisSharedPipesModule, OrionModule, FeatureToggleModule, RequestFeedbackButtonComponent], - declarations: [ExerciseDetailsStudentActionsComponent, OrionExerciseDetailsStudentActionsComponent], + imports: [ + ArtemisSharedModule, + ArtemisSharedComponentModule, + ArtemisSharedPipesModule, + OrionModule, + RequestFeedbackButtonComponent, + ExerciseDetailsStudentActionsComponent, + OrionExerciseDetailsStudentActionsComponent, + ], exports: [ExerciseDetailsStudentActionsComponent, OrionExerciseDetailsStudentActionsComponent], }) export class ArtemisExerciseButtonsModule {} diff --git a/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html b/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html index 6b7f975bec37..4e4b195b8a3a 100644 --- a/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html +++ b/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.html @@ -107,40 +107,41 @@ /> } + code editor button. We should think about defining a component that contains only the code button. --> @if (shouldDisplayIDEButtons()) { - - @if (!examMode && programmingExercise?.allowOnlineEditor) { - - } - @if (programmingExercise?.allowOfflineIde) { - - } - @if (theiaEnabled) { - - - - - } - @if (exercise.allowFeedbackRequests && gradedParticipation && exercise.type === ExerciseType.PROGRAMMING) { - - } - + + + @if (!examMode && programmingExercise?.allowOnlineEditor) { + + } + @if (programmingExercise?.allowOfflineIde) { + + } + @if (theiaEnabled) { + + + + + } + @if (exercise.allowFeedbackRequests && gradedParticipation && exercise.type === ExerciseType.PROGRAMMING) { + + } + } @if ( diff --git a/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts b/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts index 97dc98b15681..1f6cb4f16253 100644 --- a/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts +++ b/src/main/webapp/app/overview/exercise-details/exercise-details-student-actions.component.ts @@ -1,5 +1,5 @@ -import { Component, ContentChild, EventEmitter, HostBinding, Input, OnChanges, OnInit, Output, TemplateRef } from '@angular/core'; -import { Router } from '@angular/router'; +import { Component, EventEmitter, HostBinding, Input, OnChanges, OnInit, Output, inject } from '@angular/core'; +import { Router, RouterLink } from '@angular/router'; import { AlertService } from 'app/core/util/alert.service'; import { ExternalCloningService } from 'app/exercises/programming/shared/service/external-cloning.service'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; @@ -11,9 +11,8 @@ import { ProgrammingExercise } from 'app/entities/programming/programming-exerci import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ArtemisQuizService } from 'app/shared/quiz/quiz.service'; import { finalize } from 'rxjs/operators'; -import { faCodeBranch, faDesktop, faEye, faFolderOpen, faPenSquare, faPlayCircle, faRedo, faUsers } from '@fortawesome/free-solid-svg-icons'; +import { faDesktop, faEye, faFolderOpen, faPlayCircle, faRedo, faUsers } from '@fortawesome/free-solid-svg-icons'; import { CourseExerciseService } from 'app/exercises/shared/course-exercises/course-exercise.service'; -import { TranslateService } from '@ngx-translate/core'; import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; import dayjs from 'dayjs/esm'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; @@ -21,14 +20,47 @@ import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { PROFILE_ATHENA, PROFILE_LOCALVC, PROFILE_THEIA } from 'app/app.constants'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { ButtonType } from 'app/shared/components/button.component'; +import { NgTemplateOutlet } from '@angular/common'; +import { ExerciseActionButtonComponent } from 'app/shared/components/exercise-action-button.component'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FeatureToggleDirective } from 'app/shared/feature-toggle/feature-toggle.directive'; +import { StartPracticeModeButtonComponent } from 'app/shared/components/start-practice-mode-button/start-practice-mode-button.component'; +import { OpenCodeEditorButtonComponent } from 'app/shared/components/open-code-editor-button/open-code-editor-button.component'; +import { CodeButtonComponent } from 'app/shared/components/code-button/code-button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { RequestFeedbackButtonComponent } from './request-feedback-button/request-feedback-button.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ + imports: [ + NgTemplateOutlet, + ExerciseActionButtonComponent, + RouterLink, + NgbTooltip, + FeatureToggleDirective, + StartPracticeModeButtonComponent, + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // ExtensionPointDirective, + OpenCodeEditorButtonComponent, + CodeButtonComponent, + FaIconComponent, + TranslateDirective, + RequestFeedbackButtonComponent, + ArtemisTranslatePipe, + ], + providers: [ExternalCloningService], selector: 'jhi-exercise-details-student-actions', - templateUrl: './exercise-details-student-actions.component.html', styleUrls: ['../course-overview.scss'], - providers: [ExternalCloningService], + templateUrl: './exercise-details-student-actions.component.html', }) export class ExerciseDetailsStudentActionsComponent implements OnInit, OnChanges { + private alertService = inject(AlertService); + private courseExerciseService = inject(CourseExerciseService); + private router = inject(Router); + private participationService = inject(ParticipationService); + private profileService = inject(ProfileService); + readonly FeatureToggle = FeatureToggle; readonly ExerciseType = ExerciseType; readonly InitializationState = InitializationState; @@ -46,7 +78,8 @@ export class ExerciseDetailsStudentActionsComponent implements OnInit, OnChanges @Output() generatingFeedback: EventEmitter = new EventEmitter(); // extension points, see shared/extension-point - @ContentChild('overrideCodeAndOnlineEditorButton') overrideCodeAndOnlineEditorButton: TemplateRef; + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // @ContentChild('overrideCodeAndOnlineEditorButton') overrideCodeAndOnlineEditorButton: TemplateRef; uninitializedQuiz: boolean; quizNotStarted: boolean; @@ -62,7 +95,7 @@ export class ExerciseDetailsStudentActionsComponent implements OnInit, OnChanges routerLink: string; repositoryLink: string; - theiaEnabled: boolean = false; + theiaEnabled = false; theiaPortalURL: string; // Icons @@ -71,20 +104,7 @@ export class ExerciseDetailsStudentActionsComponent implements OnInit, OnChanges readonly faEye = faEye; readonly faPlayCircle = faPlayCircle; readonly faRedo = faRedo; - readonly faCodeBranch = faCodeBranch; readonly faDesktop = faDesktop; - readonly faPenSquare = faPenSquare; - - private feedbackSent = false; - - constructor( - private alertService: AlertService, - private courseExerciseService: CourseExerciseService, - private router: Router, - private translateService: TranslateService, - private participationService: ParticipationService, - private profileService: ProfileService, - ) {} ngOnInit(): void { this.repositoryLink = this.router.url; @@ -308,8 +328,4 @@ export class ExerciseDetailsStudentActionsComponent implements OnInit, OnChanges const participations = this.exercise.studentParticipations; return participations?.length ? participations[0].team?.id : this.exercise.studentAssignedTeamId; } - - buildPlanUrl(participation: StudentParticipation) { - return (participation as ProgrammingExerciseStudentParticipation).buildPlanUrl; - } } diff --git a/src/main/webapp/app/overview/exercise-details/lti-initializer-modal.component.ts b/src/main/webapp/app/overview/exercise-details/lti-initializer-modal.component.ts index 0bb7a8b46d5a..69e4cd290199 100644 --- a/src/main/webapp/app/overview/exercise-details/lti-initializer-modal.component.ts +++ b/src/main/webapp/app/overview/exercise-details/lti-initializer-modal.component.ts @@ -1,11 +1,15 @@ import { Component, inject } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { CopyIconButtonComponent } from 'app/shared/components/copy-icon-button/copy-icon-button.component'; @Component({ selector: 'jhi-lti-initializer-modal', templateUrl: './lti-initializer-modal.component.html', + imports: [TranslateDirective, FormsModule, CopyIconButtonComponent, RouterLink], }) export class LtiInitializerModalComponent { private activeModal = inject(NgbActiveModal); diff --git a/src/main/webapp/app/overview/exercise-details/problem-statement/problem-statement.component.ts b/src/main/webapp/app/overview/exercise-details/problem-statement/problem-statement.component.ts index 3b70909f554e..4ca8c6409f23 100644 --- a/src/main/webapp/app/overview/exercise-details/problem-statement/problem-statement.component.ts +++ b/src/main/webapp/app/overview/exercise-details/problem-statement/problem-statement.component.ts @@ -1,29 +1,31 @@ import { HttpResponse } from '@angular/common/http'; -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ExerciseDetailsType, ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; +import { ProgrammingExerciseInstructionComponent } from 'app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-problem-statement', templateUrl: './problem-statement.component.html', styleUrls: ['../../course-overview.scss'], + imports: [ProgrammingExerciseInstructionComponent, TranslateDirective, HtmlForMarkdownPipe], }) export class ProblemStatementComponent implements OnInit { + private route = inject(ActivatedRoute); + private exerciseService = inject(ExerciseService); + private participationService = inject(ParticipationService); + @Input() public exercise?: Exercise; @Input() participation?: StudentParticipation; - constructor( - private route: ActivatedRoute, - private exerciseService: ExerciseService, - private participationService: ParticipationService, - ) {} - ngOnInit() { this.route.params.subscribe((params) => { const exerciseId = parseInt(params['exerciseId'], 10); diff --git a/src/main/webapp/app/overview/exercise-details/request-feedback-button/request-feedback-button.component.ts b/src/main/webapp/app/overview/exercise-details/request-feedback-button/request-feedback-button.component.ts index 0f0ec10c1c14..f73c6ba0105d 100644 --- a/src/main/webapp/app/overview/exercise-details/request-feedback-button/request-feedback-button.component.ts +++ b/src/main/webapp/app/overview/exercise-details/request-feedback-button/request-feedback-button.component.ts @@ -18,7 +18,6 @@ import { ParticipationService } from 'app/exercises/shared/participation/partici @Component({ selector: 'jhi-request-feedback-button', - standalone: true, imports: [CommonModule, ArtemisSharedCommonModule, NgbTooltipModule, FontAwesomeModule], templateUrl: './request-feedback-button.component.html', }) diff --git a/src/main/webapp/app/overview/header-course.component.ts b/src/main/webapp/app/overview/header-course.component.ts index 3cdbdb04c465..d698b4d429ce 100644 --- a/src/main/webapp/app/overview/header-course.component.ts +++ b/src/main/webapp/app/overview/header-course.component.ts @@ -1,16 +1,24 @@ -import { Component, HostListener, Input, OnChanges } from '@angular/core'; +import { Component, HostListener, Input, OnChanges, inject } from '@angular/core'; import { Course } from 'app/entities/course.model'; import { ARTEMIS_DEFAULT_COLOR } from 'app/app.constants'; import { CachingStrategy } from 'app/shared/image/secured-image.component'; -import { Router } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; import { faArrowDown } from '@fortawesome/free-solid-svg-icons'; +import { NgStyle } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from '../shared/language/translate.directive'; +import { SecuredImageComponent } from '../shared/image/secured-image.component'; +import { ArtemisTranslatePipe } from '../shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-header-course', templateUrl: './header-course.component.html', styleUrls: ['./header-course.component.scss'], + imports: [NgStyle, FaIconComponent, TranslateDirective, RouterLink, SecuredImageComponent, ArtemisTranslatePipe], }) export class HeaderCourseComponent implements OnChanges { + protected router = inject(Router); + readonly ARTEMIS_DEFAULT_COLOR = ARTEMIS_DEFAULT_COLOR; readonly CachingStrategy = CachingStrategy; @@ -21,7 +29,6 @@ export class HeaderCourseComponent implements OnChanges { public longDescriptionShown = false; faArrowDown = faArrowDown; - constructor(protected router: Router) {} ngOnChanges() { this.adjustCourseDescription(); diff --git a/src/main/webapp/app/overview/participation-websocket.service.ts b/src/main/webapp/app/overview/participation-websocket.service.ts index 3836e7a81209..7e7f40e98f8d 100644 --- a/src/main/webapp/app/overview/participation-websocket.service.ts +++ b/src/main/webapp/app/overview/participation-websocket.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BehaviorSubject, of, pipe } from 'rxjs'; import { switchMap, tap } from 'rxjs/operators'; import { Participation } from 'app/entities/participation/participation.model'; @@ -25,6 +25,9 @@ export interface IParticipationWebsocketService { @Injectable({ providedIn: 'root' }) export class ParticipationWebsocketService implements IParticipationWebsocketService { + private jhiWebsocketService = inject(JhiWebsocketService); + private participationService = inject(ParticipationService); + cachedParticipations: Map = new Map(); openResultWebsocketSubscriptions: Map = new Map(); openPersonalWebsocketSubscription?: string; /* url of websocket connection */ @@ -33,11 +36,6 @@ export class ParticipationWebsocketService implements IParticipationWebsocketSer subscribedExercises: Map /* IDs of the participations of this exercise */> = new Map>(); participationSubscriptionTypes: Map = new Map(); - constructor( - private jhiWebsocketService: JhiWebsocketService, - private participationService: ParticipationService, - ) {} - private getNotifyAllSubscribersPipe = () => { return pipe(tap(this.notifyResultSubscribers), switchMap(this.addResultToParticipation), tap(this.notifyParticipationSubscribers)); }; diff --git a/src/main/webapp/app/overview/result-history/result-history.component.ts b/src/main/webapp/app/overview/result-history/result-history.component.ts index ded0d2c95639..72db3215285b 100644 --- a/src/main/webapp/app/overview/result-history/result-history.component.ts +++ b/src/main/webapp/app/overview/result-history/result-history.component.ts @@ -2,6 +2,9 @@ import { Component, OnChanges, input } from '@angular/core'; import { Result } from 'app/entities/result.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { MissingResultInformation, evaluateTemplateStatus, getResultIconClass, getTextColorClass } from 'app/exercises/shared/result/result.utils'; +import { NgClass, NgStyle } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ResultComponent } from '../../exercises/shared/result/result.component'; export const MAX_RESULT_HISTORY_LENGTH = 5; @@ -10,6 +13,7 @@ export const MAX_RESULT_HISTORY_LENGTH = 5; selector: 'jhi-result-history', templateUrl: './result-history.component.html', styleUrls: ['./result-history.scss'], + imports: [NgClass, FaIconComponent, ResultComponent, NgStyle], }) export class ResultHistoryComponent implements OnChanges { readonly getTextColorClass = getTextColorClass; @@ -25,7 +29,7 @@ export class ResultHistoryComponent implements OnChanges { displayedResults: Result[]; movedLastRatedResult: boolean; - ngOnChanges(): void { + ngOnChanges() { this.showPreviousDivider = this.results().length > MAX_RESULT_HISTORY_LENGTH; if (this.exercise()?.type === ExerciseType.TEXT || this.exercise()?.type === ExerciseType.MODELING) { this.displayedResults = this.results().filter((result) => result.successful !== undefined); diff --git a/src/main/webapp/app/overview/submission-result-status.component.ts b/src/main/webapp/app/overview/submission-result-status.component.ts index 78fc9b3a0a4d..ce737d23d33f 100644 --- a/src/main/webapp/app/overview/submission-result-status.component.ts +++ b/src/main/webapp/app/overview/submission-result-status.component.ts @@ -5,10 +5,14 @@ import { InitializationState } from 'app/entities/participation/participation.mo import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { ArtemisQuizService } from 'app/shared/quiz/quiz.service'; import dayjs from 'dayjs/esm'; +import { UpdatingResultComponent } from '../exercises/shared/result/updating-result.component'; +import { TranslateDirective } from '../shared/language/translate.directive'; +import { ProgrammingExerciseStudentTriggerBuildButtonComponent } from '../exercises/programming/shared/actions/programming-exercise-student-trigger-build-button.component'; @Component({ selector: 'jhi-submission-result-status', templateUrl: './submission-result-status.component.html', + imports: [UpdatingResultComponent, TranslateDirective, ProgrammingExerciseStudentTriggerBuildButtonComponent], }) export class SubmissionResultStatusComponent implements OnChanges { private readonly initializationStatesToShowProgrammingResult = [InitializationState.INITIALIZED, InitializationState.INACTIVE, InitializationState.FINISHED]; diff --git a/src/main/webapp/app/overview/submission-result-status.module.ts b/src/main/webapp/app/overview/submission-result-status.module.ts index eb16329d6fc5..8e5c1b8b4257 100644 --- a/src/main/webapp/app/overview/submission-result-status.module.ts +++ b/src/main/webapp/app/overview/submission-result-status.module.ts @@ -8,8 +8,15 @@ import { ArtemisSharedComponentModule } from 'app/shared/components/shared-compo import { ResultProgressBarComponent } from 'app/exercises/shared/result/result-progress-bar/result-progress-bar.component'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisProgrammingExerciseActionsModule, ArtemisSharedComponentModule, ResultProgressBarComponent], - declarations: [SubmissionResultStatusComponent, UpdatingResultComponent, ResultComponent], + imports: [ + ArtemisSharedModule, + ArtemisProgrammingExerciseActionsModule, + ArtemisSharedComponentModule, + ResultProgressBarComponent, + SubmissionResultStatusComponent, + UpdatingResultComponent, + ResultComponent, + ], exports: [SubmissionResultStatusComponent, UpdatingResultComponent, ResultComponent], }) export class SubmissionResultStatusModule {} diff --git a/src/main/webapp/app/overview/tutorial-group-details/course-tutorial-group-detail/course-tutorial-group-detail.component.ts b/src/main/webapp/app/overview/tutorial-group-details/course-tutorial-group-detail/course-tutorial-group-detail.component.ts index 047a8426ddee..287910f0c081 100644 --- a/src/main/webapp/app/overview/tutorial-group-details/course-tutorial-group-detail/course-tutorial-group-detail.component.ts +++ b/src/main/webapp/app/overview/tutorial-group-details/course-tutorial-group-detail/course-tutorial-group-detail.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { Course } from 'app/entities/course.model'; import { ActivatedRoute } from '@angular/router'; @@ -9,13 +9,23 @@ import { HttpErrorResponse } from '@angular/common/http'; import { onError } from 'app/shared/util/global.utils'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { AsyncPipe, NgClass } from '@angular/common'; +import { TutorialGroupDetailComponent } from 'app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component'; @Component({ selector: 'jhi-course-tutorial-group-detail', templateUrl: './course-tutorial-group-detail.component.html', styleUrls: ['./course-tutorial-group-detail.component.scss'], + imports: [LoadingIndicatorContainerComponent, NgClass, TutorialGroupDetailComponent, AsyncPipe], }) export class CourseTutorialGroupDetailComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private tutorialGroupService = inject(TutorialGroupsService); + private alertService = inject(AlertService); + private courseManagementService = inject(CourseManagementService); + private profileService = inject(ProfileService); + isLoading$ = new BehaviorSubject(false); tutorialGroup?: TutorialGroup; course?: Course; @@ -24,14 +34,6 @@ export class CourseTutorialGroupDetailComponent implements OnInit, OnDestroy { isProduction = true; isTestServer = false; - constructor( - private route: ActivatedRoute, - private tutorialGroupService: TutorialGroupsService, - private alertService: AlertService, - private courseManagementService: CourseManagementService, - private profileService: ProfileService, - ) {} - ngOnInit(): void { const courseIdParams$ = this.route.parent?.parent?.params; const tutorialGroupIdParams$ = this.route.params; diff --git a/src/main/webapp/app/overview/tutorial-group-details/course-tutorial-group-details.module.ts b/src/main/webapp/app/overview/tutorial-group-details/course-tutorial-group-details.module.ts index 090a381aa1c8..ae2ddcac2112 100644 --- a/src/main/webapp/app/overview/tutorial-group-details/course-tutorial-group-details.module.ts +++ b/src/main/webapp/app/overview/tutorial-group-details/course-tutorial-group-details.module.ts @@ -1,15 +1,17 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { ArtemisTutorialGroupsSharedModule } from 'app/course/tutorial-groups/shared/tutorial-groups-shared.module'; -import { CourseTutorialGroupDetailComponent } from 'app/overview/tutorial-group-details/course-tutorial-group-detail/course-tutorial-group-detail.component'; + import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; +import { CourseTutorialGroupDetailComponent } from 'app/overview/tutorial-group-details/course-tutorial-group-detail/course-tutorial-group-detail.component'; import { Authority } from 'app/shared/constants/authority.constants'; import { ArtemisSharedModule } from 'app/shared/shared.module'; const routes: Routes = [ { path: '', - component: CourseTutorialGroupDetailComponent, + loadComponent: () => + import('app/overview/tutorial-group-details/course-tutorial-group-detail/course-tutorial-group-detail.component').then((m) => m.CourseTutorialGroupDetailComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.pages.courseTutorialGroupDetail.title', @@ -19,7 +21,6 @@ const routes: Routes = [ ]; @NgModule({ - imports: [ArtemisTutorialGroupsSharedModule, RouterModule.forChild(routes), ArtemisSharedModule], - declarations: [CourseTutorialGroupDetailComponent], + imports: [ArtemisTutorialGroupsSharedModule, RouterModule.forChild(routes), ArtemisSharedModule, CourseTutorialGroupDetailComponent], }) export class CourseTutorialGroupDetailsModule {} diff --git a/src/main/webapp/app/overview/visualizations/exercise-scores-chart.module.ts b/src/main/webapp/app/overview/visualizations/exercise-scores-chart.module.ts index e0d3a22862c3..905cb064fb62 100644 --- a/src/main/webapp/app/overview/visualizations/exercise-scores-chart.module.ts +++ b/src/main/webapp/app/overview/visualizations/exercise-scores-chart.module.ts @@ -6,8 +6,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { LineChartModule } from '@swimlane/ngx-charts'; @NgModule({ - imports: [TranslateModule, CommonModule, ArtemisSharedModule, LineChartModule], - declarations: [ExerciseScoresChartComponent], + imports: [TranslateModule, CommonModule, ArtemisSharedModule, LineChartModule, ExerciseScoresChartComponent], exports: [ExerciseScoresChartComponent], }) export class ArtemisExerciseScoresChartModule {} diff --git a/src/main/webapp/app/overview/visualizations/exercise-scores-chart.service.ts b/src/main/webapp/app/overview/visualizations/exercise-scores-chart.service.ts index d92ca47740e8..e35422b48016 100644 --- a/src/main/webapp/app/overview/visualizations/exercise-scores-chart.service.ts +++ b/src/main/webapp/app/overview/visualizations/exercise-scores-chart.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import dayjs from 'dayjs/esm'; @@ -23,9 +23,10 @@ export class ExerciseScoresDTO { */ @Injectable({ providedIn: 'root' }) export class ExerciseScoresChartService { + private http = inject(HttpClient); + public resourceUrl = 'api'; - constructor(private http: HttpClient) {} /** * Get the course exercise performance statistics necessary for exercise-scores-chart.component.ts * @param courseId id of the course diff --git a/src/main/webapp/app/overview/visualizations/exercise-scores-chart/exercise-scores-chart.component.ts b/src/main/webapp/app/overview/visualizations/exercise-scores-chart/exercise-scores-chart.component.ts index 6d99a9962df0..bea8acee193f 100644 --- a/src/main/webapp/app/overview/visualizations/exercise-scores-chart/exercise-scores-chart.component.ts +++ b/src/main/webapp/app/overview/visualizations/exercise-scores-chart/exercise-scores-chart.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, Input, OnChanges } from '@angular/core'; +import { AfterViewInit, Component, Input, OnChanges, inject } from '@angular/core'; import { ExerciseScoresChartService, ExerciseScoresDTO } from 'app/overview/visualizations/exercise-scores-chart.service'; import { AlertService } from 'app/core/util/alert.service'; import { onError } from 'app/shared/util/global.utils'; @@ -7,13 +7,17 @@ import { HttpErrorResponse } from '@angular/common/http'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { cloneDeep, sortBy } from 'lodash-es'; -import { Color, ScaleType } from '@swimlane/ngx-charts'; +import { Color, LineChartModule, ScaleType } from '@swimlane/ngx-charts'; import { round } from 'app/shared/util/utils'; import { ExerciseType } from 'app/entities/exercise.model'; import { faFilter } from '@fortawesome/free-solid-svg-icons'; import { ChartExerciseTypeFilter } from 'app/shared/chart/chart-exercise-type-filter'; import { GraphColors } from 'app/entities/statistics.model'; import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbDropdown, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; type ChartSeries = { name: string; @@ -35,8 +39,16 @@ type SeriesDatapoint = { selector: 'jhi-exercise-scores-chart', templateUrl: './exercise-scores-chart.component.html', styleUrls: ['./exercise-scores-chart.component.scss'], + imports: [TranslateDirective, NgbDropdown, NgbDropdownToggle, FaIconComponent, NgbDropdownMenu, LineChartModule, ArtemisTranslatePipe], }) export class ExerciseScoresChartComponent implements AfterViewInit, OnChanges { + private navigationUtilService = inject(ArtemisNavigationUtilService); + private activatedRoute = inject(ActivatedRoute); + private alertService = inject(AlertService); + private exerciseScoresChartService = inject(ExerciseScoresChartService); + exerciseTypeFilter = inject(ChartExerciseTypeFilter); + private translateService = inject(TranslateService); + @Input() filteredExerciseIDs: number[]; courseId: number; @@ -69,14 +81,7 @@ export class ExerciseScoresChartComponent implements AfterViewInit, OnChanges { maximumScoreLabel: string; maxScale = 101; - constructor( - private navigationUtilService: ArtemisNavigationUtilService, - private activatedRoute: ActivatedRoute, - private alertService: AlertService, - private exerciseScoresChartService: ExerciseScoresChartService, - public exerciseTypeFilter: ChartExerciseTypeFilter, // used in html, therefore it must be public - private translateService: TranslateService, - ) { + constructor() { this.translateService.onLangChange.subscribe(() => { this.setTranslations(); }); @@ -91,7 +96,7 @@ export class ExerciseScoresChartComponent implements AfterViewInit, OnChanges { }); } - ngOnChanges(): void { + ngOnChanges() { this.initializeChart(); } diff --git a/src/main/webapp/app/shared/additional-feedback/additional-feedback.component.ts b/src/main/webapp/app/shared/additional-feedback/additional-feedback.component.ts index 2c6083340846..f1d9fee0f27b 100644 --- a/src/main/webapp/app/shared/additional-feedback/additional-feedback.component.ts +++ b/src/main/webapp/app/shared/additional-feedback/additional-feedback.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { Feedback, buildFeedbackTextForReview } from 'app/entities/feedback.model'; import { getCourseFromExercise } from 'app/entities/exercise.model'; import { Course } from 'app/entities/course.model'; @@ -6,19 +6,24 @@ import { faCommentDots } from '@fortawesome/free-regular-svg-icons'; import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import { LocaleConversionService } from 'app/shared/service/locale-conversion.service'; import { TranslateService } from '@ngx-translate/core'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from '../language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-additional-feedback', templateUrl: './additional-feedback.component.html', styleUrls: ['./additional-feedback.component.scss'], + imports: [FaIconComponent, TranslateDirective, NgbTooltip, ArtemisTranslatePipe], }) export class AdditionalFeedbackComponent { - @Input() - feedback: Feedback[]; - @Input() - additional: boolean; - @Input() - course?: Course; + private translateService = inject(TranslateService); + private localeConversionService = inject(LocaleConversionService); + + @Input() feedback: Feedback[]; + @Input() additional: boolean; + @Input() course?: Course; // Icons faCommentDots = faCommentDots; @@ -28,11 +33,6 @@ export class AdditionalFeedbackComponent { readonly getCourseFromExercise = getCourseFromExercise; readonly buildFeedbackTextForReview = buildFeedbackTextForReview; - constructor( - private translateService: TranslateService, - private localeConversionService: LocaleConversionService, - ) {} - /** * Translates the points string based on the singularity of the given points. * In addition, the points are returned in a localized form. diff --git a/src/main/webapp/app/shared/alert/alert-overlay.component.ts b/src/main/webapp/app/shared/alert/alert-overlay.component.ts index c1a18cb51250..d9d8ac765959 100644 --- a/src/main/webapp/app/shared/alert/alert-overlay.component.ts +++ b/src/main/webapp/app/shared/alert/alert-overlay.component.ts @@ -1,7 +1,11 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { Alert, AlertService } from 'app/core/util/alert.service'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { animate, group, style, transition, trigger } from '@angular/animations'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from '../language/translate.directive'; +import { NgClass, NgStyle } from '@angular/common'; +import { CloseCircleComponent } from '../close-circle/close-circle.component'; @Component({ selector: 'jhi-alert-overlay', @@ -44,11 +48,12 @@ import { animate, group, style, transition, trigger } from '@angular/animations' ]), ]), ], + imports: [FaIconComponent, TranslateDirective, NgClass, CloseCircleComponent, NgStyle], }) export class AlertOverlayComponent implements OnInit, OnDestroy { - alerts: Alert[] = []; + alertService = inject(AlertService); - constructor(public alertService: AlertService) {} + alerts: Alert[] = []; times = faTimes; diff --git a/src/main/webapp/app/shared/auth/has-any-authority.directive.ts b/src/main/webapp/app/shared/auth/has-any-authority.directive.ts index e0c1abfcd8cc..9d7eef8a8704 100644 --- a/src/main/webapp/app/shared/auth/has-any-authority.directive.ts +++ b/src/main/webapp/app/shared/auth/has-any-authority.directive.ts @@ -1,4 +1,4 @@ -import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core'; +import { Directive, Input, TemplateRef, ViewContainerRef, inject } from '@angular/core'; import { AccountService } from 'app/core/auth/account.service'; /** @@ -12,17 +12,13 @@ import { AccountService } from 'app/core/auth/account.service'; * ... * ``` */ -@Directive({ - selector: '[jhiHasAnyAuthority]', -}) +@Directive({ selector: '[jhiHasAnyAuthority]' }) export class HasAnyAuthorityDirective { - private authorities: string[]; + private accountService = inject(AccountService); + private templateRef = inject>(TemplateRef); + private viewContainerRef = inject(ViewContainerRef); - constructor( - private accountService: AccountService, - private templateRef: TemplateRef, - private viewContainerRef: ViewContainerRef, - ) {} + private authorities: string[]; @Input() set jhiHasAnyAuthority(value: string | string[]) { diff --git a/src/main/webapp/app/shared/breakpoints/layout.service.ts b/src/main/webapp/app/shared/breakpoints/layout.service.ts index 97ff7e71da29..461b674c05a5 100644 --- a/src/main/webapp/app/shared/breakpoints/layout.service.ts +++ b/src/main/webapp/app/shared/breakpoints/layout.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BreakpointObserver } from '@angular/cdk/layout'; import { BreakpointsService } from './breakpoints.service'; import { map } from 'rxjs/operators'; @@ -8,12 +8,10 @@ import { Observable } from 'rxjs'; providedIn: 'root', }) export class LayoutService { - activeBreakpoints: string[] = []; + private breakpointObserver = inject(BreakpointObserver); + private breakpointService = inject(BreakpointsService); - constructor( - private breakpointObserver: BreakpointObserver, - private breakpointService: BreakpointsService, - ) {} + activeBreakpoints: string[] = []; subscribeToLayoutChanges(): Observable { return this.breakpointObserver.observe(this.breakpointService.getBreakpoints()).pipe(map((observeResponse) => this.parseBreakpointsResponse(observeResponse.breakpoints))); diff --git a/src/main/webapp/app/shared/category-selector/category-selector.component.ts b/src/main/webapp/app/shared/category-selector/category-selector.component.ts index 2c3e31b1e67f..f5e5bb76adcf 100644 --- a/src/main/webapp/app/shared/category-selector/category-selector.component.ts +++ b/src/main/webapp/app/shared/category-selector/category-selector.component.ts @@ -1,13 +1,18 @@ import { Component, ElementRef, EventEmitter, Input, OnChanges, Output, ViewChild, ViewEncapsulation } from '@angular/core'; import { ColorSelectorComponent } from 'app/shared/color-selector/color-selector.component'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; -import { MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete'; +import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { COMMA, ENTER, TAB } from '@angular/cdk/keycodes'; -import { FormControl } from '@angular/forms'; -import { MatChipInputEvent } from '@angular/material/chips'; +import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRemove, MatChipRow } from '@angular/material/chips'; import { Observable, map, startWith } from 'rxjs'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import { FaqCategory } from 'app/entities/faq-category.model'; +import { MatFormField } from '@angular/material/form-field'; +import { AsyncPipe, NgStyle } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { MatOption } from '@angular/material/core'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; const DEFAULT_COLORS = ['#6ae8ac', '#9dca53', '#94a11c', '#691b0b', '#ad5658', '#1b97ca', '#0d3cc2', '#0ab84f']; @@ -16,6 +21,23 @@ const DEFAULT_COLORS = ['#6ae8ac', '#9dca53', '#94a11c', '#691b0b', '#ad5658', ' templateUrl: './category-selector.component.html', styleUrls: ['./category-selector.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + MatFormField, + MatChipGrid, + MatChipRow, + NgStyle, + MatChipRemove, + FaIconComponent, + FormsModule, + MatAutocompleteTrigger, + MatChipInput, + ReactiveFormsModule, + MatAutocomplete, + MatOption, + ColorSelectorComponent, + AsyncPipe, + ArtemisTranslatePipe, + ], }) export class CategorySelectorComponent implements OnChanges { protected readonly faTimes = faTimes; diff --git a/src/main/webapp/app/shared/category-selector/category-selector.module.ts b/src/main/webapp/app/shared/category-selector/category-selector.module.ts index 9b9ed1a5f0e5..a114b2c98792 100644 --- a/src/main/webapp/app/shared/category-selector/category-selector.module.ts +++ b/src/main/webapp/app/shared/category-selector/category-selector.module.ts @@ -10,8 +10,17 @@ import { MatSelectModule } from '@angular/material/select'; import { MatFormFieldModule } from '@angular/material/form-field'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisColorSelectorModule, ReactiveFormsModule, FormsModule, MatChipsModule, MatAutocompleteModule, MatSelectModule, MatFormFieldModule], - declarations: [CategorySelectorComponent], + imports: [ + ArtemisSharedModule, + ArtemisColorSelectorModule, + ReactiveFormsModule, + FormsModule, + MatChipsModule, + MatAutocompleteModule, + MatSelectModule, + MatFormFieldModule, + CategorySelectorComponent, + ], exports: [CategorySelectorComponent], }) export class ArtemisCategorySelectorModule {} diff --git a/src/main/webapp/app/shared/chart/artemis-charts.module.ts b/src/main/webapp/app/shared/chart/artemis-charts.module.ts index f95d838f1b04..1072378c1b74 100644 --- a/src/main/webapp/app/shared/chart/artemis-charts.module.ts +++ b/src/main/webapp/app/shared/chart/artemis-charts.module.ts @@ -8,8 +8,17 @@ import { RouterModule } from '@angular/router'; import { BarChartModule, LineChartModule, PieChartModule } from '@swimlane/ngx-charts'; @NgModule({ - imports: [ArtemisSharedModule, RouterModule, BarChartModule, LineChartModule, PieChartModule], - declarations: [DoughnutChartComponent, StatisticsAverageScoreGraphComponent, StatisticsGraphComponent, StatisticsScoreDistributionGraphComponent], + imports: [ + ArtemisSharedModule, + RouterModule, + BarChartModule, + LineChartModule, + PieChartModule, + DoughnutChartComponent, + StatisticsAverageScoreGraphComponent, + StatisticsGraphComponent, + StatisticsScoreDistributionGraphComponent, + ], exports: [DoughnutChartComponent, StatisticsAverageScoreGraphComponent, StatisticsGraphComponent, StatisticsScoreDistributionGraphComponent], }) export class ArtemisChartsModule {} diff --git a/src/main/webapp/app/shared/circular-progress-bar/circular-progress-bar.component.ts b/src/main/webapp/app/shared/circular-progress-bar/circular-progress-bar.component.ts index f3669b92065b..6b7a547f41e1 100644 --- a/src/main/webapp/app/shared/circular-progress-bar/circular-progress-bar.component.ts +++ b/src/main/webapp/app/shared/circular-progress-bar/circular-progress-bar.component.ts @@ -15,8 +15,6 @@ export class CircularProgressBarComponent implements OnChanges { progressText = 'Completed'; circleColor = '#000000'; - constructor() {} - ngOnChanges() { if (this.progressInPercent > 100) { this.progressUsedForColorCalculation = 100; diff --git a/src/main/webapp/app/shared/color-selector/color-selector.component.ts b/src/main/webapp/app/shared/color-selector/color-selector.component.ts index 91061dc28f04..ce81d215aa95 100644 --- a/src/main/webapp/app/shared/color-selector/color-selector.component.ts +++ b/src/main/webapp/app/shared/color-selector/color-selector.component.ts @@ -1,5 +1,6 @@ -import { Component, ElementRef, EventEmitter, Input, OnInit, Output, Renderer2 } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, OnInit, Output, Renderer2, inject } from '@angular/core'; import { ARTEMIS_DEFAULT_COLOR } from 'app/app.constants'; +import { NgStyle } from '@angular/common'; export interface Coordinates { left: number; @@ -29,19 +30,18 @@ const DEFAULT_COLORS = [ selector: 'jhi-color-selector', templateUrl: './color-selector.component.html', styleUrls: ['./color-selector.scss'], + imports: [NgStyle], }) export class ColorSelectorComponent implements OnInit { + private elementRef = inject(ElementRef); + private renderer = inject(Renderer2); + colorSelectorPosition: Coordinates; showColorSelector = false; height = 220; @Input() tagColors: string[] = DEFAULT_COLORS; @Output() selectedColor = new EventEmitter(); - constructor( - private elementRef: ElementRef, - private renderer: Renderer2, - ) {} - /** * set default position on init */ diff --git a/src/main/webapp/app/shared/color-selector/color-selector.module.ts b/src/main/webapp/app/shared/color-selector/color-selector.module.ts index 9366e250aa7a..819ffbc1df2b 100644 --- a/src/main/webapp/app/shared/color-selector/color-selector.module.ts +++ b/src/main/webapp/app/shared/color-selector/color-selector.module.ts @@ -4,8 +4,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ColorSelectorComponent } from './color-selector.component'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [ColorSelectorComponent], + imports: [ArtemisSharedModule, ColorSelectorComponent], exports: [ColorSelectorComponent], }) export class ArtemisColorSelectorModule {} diff --git a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts index 41f2dcf0e67a..c448f38ca8a2 100644 --- a/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts +++ b/src/main/webapp/app/shared/competency-selection/competency-selection.component.ts @@ -1,5 +1,5 @@ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, forwardRef } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, forwardRef, inject } from '@angular/core'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { CompetencyLearningObjectLink, @@ -15,6 +15,10 @@ import { ActivatedRoute } from '@angular/router'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { finalize } from 'rxjs'; import { CourseCompetencyService } from 'app/course/competencies/course-competency.service'; +import { FaIconComponent, FaStackComponent, FaStackItemSizeDirective } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from '../language/translate.directive'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-competency-selection', @@ -27,8 +31,14 @@ import { CourseCompetencyService } from 'app/course/competencies/course-competen useExisting: forwardRef(() => CompetencySelectionComponent), }, ], + imports: [FaStackComponent, NgbTooltip, FaIconComponent, FaStackItemSizeDirective, FormsModule, TranslateDirective, ArtemisTranslatePipe], }) export class CompetencySelectionComponent implements OnInit, ControlValueAccessor { + private route = inject(ActivatedRoute); + private courseStorageService = inject(CourseStorageService); + private courseCompetencyService = inject(CourseCompetencyService); + private changeDetector = inject(ChangeDetectorRef); + @Input() labelName: string; @Input() labelTooltip: string; @@ -53,14 +63,8 @@ export class CompetencySelectionComponent implements OnInit, ControlValueAccesso protected readonly MEDIUM_COMPETENCY_LINK_WEIGHT = MEDIUM_COMPETENCY_LINK_WEIGHT; protected readonly LOW_COMPETENCY_LINK_WEIGHT = LOW_COMPETENCY_LINK_WEIGHT; protected readonly LOW_COMPETENCY_LINK_WEIGHT_CUT_OFF = LOW_COMPETENCY_LINK_WEIGHT_CUT_OFF; // halfway between low and medium - protected readonly MEDIUM_COMPETENCY_LINK_WEIGHT_CUT_OFF = MEDIUM_COMPETENCY_LINK_WEIGHT_CUT_OFF; // halfway between medium and high - - constructor( - private route: ActivatedRoute, - private courseStorageService: CourseStorageService, - private courseCompetencyService: CourseCompetencyService, - private changeDetector: ChangeDetectorRef, - ) {} + protected readonly MEDIUM_COMPETENCY_LINK_WEIGHT_CUT_OFF = MEDIUM_COMPETENCY_LINK_WEIGHT_CUT_OFF; + // halfway between medium and high ngOnInit(): void { const courseId = Number(this.route.snapshot.paramMap.get('courseId')); diff --git a/src/main/webapp/app/shared/components/button.component.ts b/src/main/webapp/app/shared/components/button.component.ts index 5f488b280767..45587c1dbfa2 100644 --- a/src/main/webapp/app/shared/components/button.component.ts +++ b/src/main/webapp/app/shared/components/button.component.ts @@ -2,6 +2,12 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; +import { NgClass } from '@angular/common'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FeatureToggleDirective } from '../feature-toggle/feature-toggle.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from '../language/translate.directive'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; /** * enum for button types @@ -46,6 +52,7 @@ export enum TooltipPlacement { @Component({ selector: 'jhi-button', templateUrl: './button.component.html', + imports: [NgClass, NgbTooltip, FeatureToggleDirective, FaIconComponent, TranslateDirective, ArtemisTranslatePipe], }) export class ButtonComponent { @Input() btnType = ButtonType.PRIMARY; diff --git a/src/main/webapp/app/shared/components/checklist-check/checklist-check.component.ts b/src/main/webapp/app/shared/components/checklist-check/checklist-check.component.ts index 3718e0f73adf..84b458270530 100644 --- a/src/main/webapp/app/shared/components/checklist-check/checklist-check.component.ts +++ b/src/main/webapp/app/shared/components/checklist-check/checklist-check.component.ts @@ -1,10 +1,12 @@ import { Component, Input } from '@angular/core'; import { SizeProp } from '@fortawesome/fontawesome-svg-core'; import { faCheckCircle, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-checklist-check', templateUrl: './checklist-check.component.html', + imports: [FaIconComponent], }) export class ChecklistCheckComponent { protected readonly faTimes = faTimes; diff --git a/src/main/webapp/app/shared/components/code-button/code-button.component.ts b/src/main/webapp/app/shared/components/code-button/code-button.component.ts index 36046020db1b..15437f8ae64e 100644 --- a/src/main/webapp/app/shared/components/code-button/code-button.component.ts +++ b/src/main/webapp/app/shared/components/code-button/code-button.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnInit, inject } from '@angular/core'; import { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { ExternalCloningService } from 'app/exercises/programming/shared/service/external-cloning.service'; @@ -18,13 +18,49 @@ import { IdeSettingsService } from 'app/shared/user-settings/ide-preferences/ide import { Ide } from 'app/shared/user-settings/ide-preferences/ide.model'; import { SshUserSettingsService } from 'app/shared/user-settings/ssh-settings/ssh-user-settings.service'; import { UserSshPublicKey } from 'app/entities/programming/user-ssh-public-key.model'; +import { ExerciseActionButtonComponent } from '../exercise-action-button.component'; +import { FeatureToggleDirective } from '../../feature-toggle/feature-toggle.directive'; +import { NgbDropdown, NgbDropdownMenu, NgbDropdownToggle, NgbPopover } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from '../../language/translate.directive'; +import { NgClass } from '@angular/common'; +import { CdkCopyToClipboard } from '@angular/cdk/clipboard'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { RouterLink } from '@angular/router'; +import { HelpIconComponent } from '../help-icon.component'; +import { ArtemisTranslatePipe } from '../../pipes/artemis-translate.pipe'; +import { SafeUrlPipe } from 'app/shared/pipes/safe-url.pipe'; @Component({ selector: 'jhi-code-button', templateUrl: './code-button.component.html', styleUrls: ['./code-button.component.scss'], + imports: [ + ExerciseActionButtonComponent, + FeatureToggleDirective, + NgbPopover, + TranslateDirective, + NgbDropdown, + NgbDropdownToggle, + NgbDropdownMenu, + NgClass, + CdkCopyToClipboard, + FaIconComponent, + RouterLink, + HelpIconComponent, + ArtemisTranslatePipe, + SafeUrlPipe, + ], }) export class CodeButtonComponent implements OnInit, OnChanges { + private translateService = inject(TranslateService); + private externalCloningService = inject(ExternalCloningService); + private sshUserSettingsService = inject(SshUserSettingsService); + private accountService = inject(AccountService); + private profileService = inject(ProfileService); + private localStorage = inject(LocalStorageService); + private participationService = inject(ParticipationService); + private ideSettingsService = inject(IdeSettingsService); + readonly FeatureToggle = FeatureToggle; readonly ProgrammingLanguage = ProgrammingLanguage; @@ -76,17 +112,6 @@ export class CodeButtonComponent implements OnInit, OnChanges { readonly faExternalLink = faExternalLink; ideName: string; - constructor( - private translateService: TranslateService, - private externalCloningService: ExternalCloningService, - private sshUserSettingsService: SshUserSettingsService, - private accountService: AccountService, - private profileService: ProfileService, - private localStorage: LocalStorageService, - private participationService: ParticipationService, - private ideSettingsService: IdeSettingsService, - ) {} - async ngOnInit() { const user = await this.accountService.identity(); if (!user) { diff --git a/src/main/webapp/app/shared/components/confirm-autofocus-button.component.ts b/src/main/webapp/app/shared/components/confirm-autofocus-button.component.ts index eafa2aedcb26..3de259b12f99 100644 --- a/src/main/webapp/app/shared/components/confirm-autofocus-button.component.ts +++ b/src/main/webapp/app/shared/components/confirm-autofocus-button.component.ts @@ -1,14 +1,18 @@ -import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild, inject } from '@angular/core'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { htmlForMarkdown } from 'app/shared/util/markdown.conversion.util'; import { ConfirmAutofocusModalComponent } from 'app/shared/components/confirm-autofocus-modal.component'; +import { ButtonComponent } from './button.component'; @Component({ selector: 'jhi-confirm-button', templateUrl: './confirm-autofocus-button.component.html', + imports: [ButtonComponent], }) export class ConfirmAutofocusButtonComponent { + private modalService = inject(NgbModal); + @Input() icon: IconProp; @Input() title: string; @Input() tooltip: string; @@ -25,8 +29,6 @@ export class ConfirmAutofocusButtonComponent { @ViewChild('content') content?: TemplateRef; - constructor(private modalService: NgbModal) {} - /** * open confirmation modal with text and title */ diff --git a/src/main/webapp/app/shared/components/confirm-autofocus-modal.component.ts b/src/main/webapp/app/shared/components/confirm-autofocus-modal.component.ts index 567c0672ff16..53d74b778076 100644 --- a/src/main/webapp/app/shared/components/confirm-autofocus-modal.component.ts +++ b/src/main/webapp/app/shared/components/confirm-autofocus-modal.component.ts @@ -1,17 +1,21 @@ -import { Component, Input, TemplateRef } from '@angular/core'; +import { Component, Input, TemplateRef, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgTemplateOutlet } from '@angular/common'; +import { TranslateDirective } from '../language/translate.directive'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-confirm-modal', templateUrl: './confirm-autofocus-modal.component.html', + imports: [NgTemplateOutlet, TranslateDirective, ArtemisTranslatePipe], }) export class ConfirmAutofocusModalComponent { + modal = inject(NgbActiveModal); + @Input() title: string; @Input() titleTranslationParams?: Record; @Input() text: string; @Input() translateText: boolean; @Input() textIsMarkdown: boolean; @Input() contentRef?: TemplateRef; - - constructor(public modal: NgbActiveModal) {} } diff --git a/src/main/webapp/app/shared/components/copy-icon-button/copy-icon-button.component.ts b/src/main/webapp/app/shared/components/copy-icon-button/copy-icon-button.component.ts index 13259f59b364..e15ef10bdaa4 100644 --- a/src/main/webapp/app/shared/components/copy-icon-button/copy-icon-button.component.ts +++ b/src/main/webapp/app/shared/components/copy-icon-button/copy-icon-button.component.ts @@ -1,9 +1,14 @@ import { Component, Input } from '@angular/core'; import { faCopy } from '@fortawesome/free-regular-svg-icons'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { CdkCopyToClipboard } from '@angular/cdk/clipboard'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from '../../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-copy-icon-button', templateUrl: './copy-icon-button.component.html', + imports: [NgbTooltip, CdkCopyToClipboard, FaIconComponent, ArtemisTranslatePipe], }) export class CopyIconButtonComponent { @Input() copyText: string; diff --git a/src/main/webapp/app/shared/components/course-exam-archive-button/course-exam-archive-button.component.ts b/src/main/webapp/app/shared/components/course-exam-archive-button/course-exam-archive-button.component.ts index a26b939ed089..46eb02356885 100644 --- a/src/main/webapp/app/shared/components/course-exam-archive-button/course-exam-archive-button.component.ts +++ b/src/main/webapp/app/shared/components/course-exam-archive-button/course-exam-archive-button.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, TemplateRef, ViewChild, inject } from '@angular/core'; import { AlertService } from 'app/core/util/alert.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; @@ -15,6 +15,10 @@ import { Subject } from 'rxjs'; import { AccountService } from 'app/core/auth/account.service'; import { faArchive, faCircleNotch, faDownload, faEraser } from '@fortawesome/free-solid-svg-icons'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; +import { TranslateDirective } from '../../language/translate.directive'; +import { FeatureToggleDirective } from '../../feature-toggle/feature-toggle.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { DeleteButtonDirective } from '../../delete-dialog/delete-button.directive'; export type CourseExamArchiveState = { exportState: 'COMPLETED' | 'RUNNING' | 'COMPLETED_WITH_WARNINGS' | 'COMPLETED_WITH_ERRORS'; @@ -27,8 +31,17 @@ export type CourseExamArchiveState = { templateUrl: './course-exam-archive-button.component.html', styleUrls: ['./course-exam-archive-button.component.scss'], styles: [':host {display: contents}'], + imports: [TranslateDirective, FeatureToggleDirective, FaIconComponent, DeleteButtonDirective], }) export class CourseExamArchiveButtonComponent implements OnInit, OnDestroy { + private courseService = inject(CourseManagementService); + private examService = inject(ExamManagementService); + private alertService = inject(AlertService); + private websocketService = inject(JhiWebsocketService); + private translateService = inject(TranslateService); + private modalService = inject(NgbModal); + private accountService = inject(AccountService); + ButtonSize = ButtonSize; ActionType = ActionType; readonly FeatureToggle = FeatureToggle; @@ -62,16 +75,6 @@ export class CourseExamArchiveButtonComponent implements OnInit, OnDestroy { faEraser = faEraser; faArchive = faArchive; - constructor( - private courseService: CourseManagementService, - private examService: ExamManagementService, - private alertService: AlertService, - private websocketService: JhiWebsocketService, - private translateService: TranslateService, - private modalService: NgbModal, - private accountService: AccountService, - ) {} - ngOnInit() { if (!this.course && !this.exam) { // Component isn't initialized diff --git a/src/main/webapp/app/shared/components/documentation-button/documentation-button.component.ts b/src/main/webapp/app/shared/components/documentation-button/documentation-button.component.ts index 7724361d798e..3b22a87939d8 100644 --- a/src/main/webapp/app/shared/components/documentation-button/documentation-button.component.ts +++ b/src/main/webapp/app/shared/components/documentation-button/documentation-button.component.ts @@ -1,6 +1,8 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { faCircleInfo } from '@fortawesome/free-solid-svg-icons'; import { TranslateService } from '@ngx-translate/core'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; // The routes here are used to build the link to the documentation. // Therefore, it's important that they exactly match the url to the subpage of the documentation. @@ -39,16 +41,17 @@ export type DocumentationType = keyof typeof DocumentationLinks; `, + imports: [FaIconComponent, NgbTooltip], }) export class DocumentationButtonComponent { + private translateService = inject(TranslateService); + readonly BASE_URL = 'https://docs.artemis.cit.tum.de/user/'; readonly faCircleInfo = faCircleInfo; readonly DocumentationLinks = DocumentationLinks; @Input() type: DocumentationType; - constructor(private translateService: TranslateService) {} - getTooltipForType() { const typeKey = 'artemisApp.documentationLinks.' + this.type.toLowerCase(); return this.translateService.instant('artemisApp.documentationLinks.prefix') + this.translateService.instant(typeKey); diff --git a/src/main/webapp/app/shared/components/documentation-link/documentation-link.component.ts b/src/main/webapp/app/shared/components/documentation-link/documentation-link.component.ts index 86f5d19aafb6..8c483d617500 100644 --- a/src/main/webapp/app/shared/components/documentation-link/documentation-link.component.ts +++ b/src/main/webapp/app/shared/components/documentation-link/documentation-link.component.ts @@ -12,7 +12,6 @@ export type DocumentationType = keyof typeof DocumentationLinks; @Component({ selector: 'jhi-documentation-link', - standalone: true, templateUrl: './documentation-link.component.html', imports: [TranslateDirective], }) diff --git a/src/main/webapp/app/shared/components/exercise-action-button.component.ts b/src/main/webapp/app/shared/components/exercise-action-button.component.ts index a67d8a354d60..37cc076ab7da 100644 --- a/src/main/webapp/app/shared/components/exercise-action-button.component.ts +++ b/src/main/webapp/app/shared/components/exercise-action-button.component.ts @@ -1,12 +1,14 @@ import { Component, HostBinding, Input } from '@angular/core'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { faCircleNotch } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass } from '@angular/common'; @Component({ - /* eslint-disable-next-line @angular-eslint/component-selector */ selector: 'button[jhi-exercise-action-button]', templateUrl: './exercise-action-button.component.html', styleUrls: ['../../overview/course-overview.scss'], + imports: [FaIconComponent, NgClass], }) export class ExerciseActionButtonComponent { @Input() buttonIcon: IconProp; @@ -14,6 +16,7 @@ export class ExerciseActionButtonComponent { @Input() hideLabelMobile = true; @Input() overwriteDisabled = false; @Input() buttonLoading = false; + @HostBinding('class.btn-outline-primary') @Input() outlined = false; @HostBinding('class.btn-sm') @Input() smallButton = false; @HostBinding('class.btn') isButton = true; diff --git a/src/main/webapp/app/shared/components/help-icon.component.ts b/src/main/webapp/app/shared/components/help-icon.component.ts index b478c5f093aa..daf6956074e2 100644 --- a/src/main/webapp/app/shared/components/help-icon.component.ts +++ b/src/main/webapp/app/shared/components/help-icon.component.ts @@ -1,9 +1,13 @@ import { Component, input } from '@angular/core'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-help-icon', template: ` `, + imports: [FaIconComponent, NgbTooltip, ArtemisTranslatePipe], }) export class HelpIconComponent { protected readonly faQuestionCircle = faQuestionCircle; diff --git a/src/main/webapp/app/shared/components/not-released-tag.component.ts b/src/main/webapp/app/shared/components/not-released-tag.component.ts index 9fdb0a54a3a7..a6789028e7e6 100644 --- a/src/main/webapp/app/shared/components/not-released-tag.component.ts +++ b/src/main/webapp/app/shared/components/not-released-tag.component.ts @@ -1,14 +1,19 @@ import { Component, Input } from '@angular/core'; import dayjs from 'dayjs/esm'; import { Exercise } from 'app/entities/exercise.model'; +import { TranslateDirective } from '../language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-not-released-tag', templateUrl: './not-released-tag.component.html', styleUrls: ['./not-released-tag.component.scss'], + imports: [TranslateDirective, NgbTooltip, ArtemisDatePipe, ArtemisTranslatePipe], }) export class NotReleasedTagComponent { @Input() public exercise: Exercise; - @Input() public noMargin: boolean = true; + @Input() public noMargin = true; readonly dayjs = dayjs; } diff --git a/src/main/webapp/app/shared/components/open-code-editor-button/open-code-editor-button.component.ts b/src/main/webapp/app/shared/components/open-code-editor-button/open-code-editor-button.component.ts index 10b32d55360e..0f939a33b201 100644 --- a/src/main/webapp/app/shared/components/open-code-editor-button/open-code-editor-button.component.ts +++ b/src/main/webapp/app/shared/components/open-code-editor-button/open-code-editor-button.component.ts @@ -1,15 +1,24 @@ -import { Component, Input, OnChanges } from '@angular/core'; +import { Component, Input, OnChanges, inject } from '@angular/core'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { faFolderOpen } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; import { Exercise } from 'app/entities/exercise.model'; +import { ExerciseActionButtonComponent } from '../exercise-action-button.component'; +import { FeatureToggleDirective } from '../../feature-toggle/feature-toggle.directive'; +import { RouterLink } from '@angular/router'; +import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from '../../language/translate.directive'; +import { ArtemisTranslatePipe } from '../../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-open-code-editor-button', templateUrl: './open-code-editor-button.component.html', + imports: [ExerciseActionButtonComponent, FeatureToggleDirective, RouterLink, NgbPopover, TranslateDirective, ArtemisTranslatePipe], }) export class OpenCodeEditorButtonComponent implements OnChanges { + private participationService = inject(ParticipationService); + readonly FeatureToggle = FeatureToggle; @Input() @@ -27,13 +36,11 @@ export class OpenCodeEditorButtonComponent implements OnChanges { courseAndExerciseNavigationUrl: string; activeParticipation: ProgrammingExerciseStudentParticipation; - isPracticeMode: boolean = true; + isPracticeMode = true; // Icons faFolderOpen = faFolderOpen; - constructor(private participationService: ParticipationService) {} - ngOnChanges() { this.courseAndExerciseNavigationUrl = this.courseAndExerciseNavigationUrlSegment.reduce((acc, segment) => `${acc}/${segment}`); const shouldPreferPractice = this.participationService.shouldPreferPractice(this.exercise); diff --git a/src/main/webapp/app/shared/components/reset-repo-button/reset-repo-button.component.ts b/src/main/webapp/app/shared/components/reset-repo-button/reset-repo-button.component.ts index 07f7b5f7ca94..d512fac61c0e 100644 --- a/src/main/webapp/app/shared/components/reset-repo-button/reset-repo-button.component.ts +++ b/src/main/webapp/app/shared/components/reset-repo-button/reset-repo-button.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { faBackward } from '@fortawesome/free-solid-svg-icons'; import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; @@ -10,13 +10,25 @@ import { getExerciseDueDate } from 'app/exercises/shared/exercise/exercise.utils import { finalize } from 'rxjs/operators'; import { AlertService } from 'app/core/util/alert.service'; import dayjs from 'dayjs/esm'; +import { ExerciseActionButtonComponent } from '../exercise-action-button.component'; +import { FeatureToggleDirective } from '../../feature-toggle/feature-toggle.directive'; +import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from '../../language/translate.directive'; +import { ConfirmEntityNameComponent } from '../../confirm-entity-name/confirm-entity-name.component'; +import { ArtemisTranslatePipe } from '../../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-reset-repo-button', templateUrl: './reset-repo-button.component.html', styleUrls: ['./reset-repo-button.component.scss'], + imports: [ExerciseActionButtonComponent, FeatureToggleDirective, NgbPopover, FormsModule, TranslateDirective, ConfirmEntityNameComponent, ArtemisTranslatePipe], }) export class ResetRepoButtonComponent implements OnInit { + private participationService = inject(ParticipationService); + private programmingExerciseParticipationService = inject(ProgrammingExerciseParticipationService); + private alertService = inject(AlertService); + readonly FeatureToggle = FeatureToggle; readonly INITIALIZED = InitializationState.INITIALIZED; @@ -31,12 +43,6 @@ export class ResetRepoButtonComponent implements OnInit { readonly faBackward = faBackward; - constructor( - private participationService: ParticipationService, - private programmingExerciseParticipationService: ProgrammingExerciseParticipationService, - private alertService: AlertService, - ) {} - ngOnInit() { this.gradedParticipation = this.participationService.getSpecificStudentParticipation(this.participations, false); this.practiceParticipation = this.participationService.getSpecificStudentParticipation(this.participations, true); diff --git a/src/main/webapp/app/shared/components/shared-component.module.ts b/src/main/webapp/app/shared/components/shared-component.module.ts index 090ebcf99f3d..348ce82ef2b3 100644 --- a/src/main/webapp/app/shared/components/shared-component.module.ts +++ b/src/main/webapp/app/shared/components/shared-component.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; import { ChecklistCheckComponent } from 'app/shared/components/checklist-check/checklist-check.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { ConfirmAutofocusModalComponent } from 'app/shared/components/confirm-autofocus-modal.component'; import { HelpIconComponent } from 'app/shared/components/help-icon.component'; import { ButtonComponent } from 'app/shared/components/button.component'; @@ -24,8 +24,9 @@ import { ExerciseImportWrapperComponent } from 'app/exercises/shared/import/exer import { ConfirmAutofocusButtonComponent } from 'app/shared/components/confirm-autofocus-button.component'; @NgModule({ - imports: [ArtemisSharedModule, FeatureToggleModule, ClipboardModule], - declarations: [ + imports: [ + ArtemisSharedModule, + ClipboardModule, ButtonComponent, HelpIconComponent, ConfirmAutofocusButtonComponent, diff --git a/src/main/webapp/app/shared/components/start-practice-mode-button/start-practice-mode-button.component.ts b/src/main/webapp/app/shared/components/start-practice-mode-button/start-practice-mode-button.component.ts index 2601765baf68..1ec65a0fedee 100644 --- a/src/main/webapp/app/shared/components/start-practice-mode-button/start-practice-mode-button.component.ts +++ b/src/main/webapp/app/shared/components/start-practice-mode-button/start-practice-mode-button.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { finalize } from 'rxjs/operators'; import { faRedo } from '@fortawesome/free-solid-svg-icons'; @@ -9,21 +9,28 @@ import { AlertService } from 'app/core/util/alert.service'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { ParticipationService } from 'app/exercises/shared/participation/participation.service'; import { InitializationState } from 'app/entities/participation/participation.model'; +import { ExerciseActionButtonComponent } from '../exercise-action-button.component'; +import { FeatureToggleDirective } from '../../feature-toggle/feature-toggle.directive'; +import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from '../../language/translate.directive'; +import { ArtemisTranslatePipe } from '../../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-start-practice-mode-button', templateUrl: './start-practice-mode-button.component.html', styleUrls: ['./start-practice-mode-button.component.scss'], + imports: [ExerciseActionButtonComponent, FeatureToggleDirective, NgbPopover, TranslateDirective, ArtemisTranslatePipe], }) export class StartPracticeModeButtonComponent implements OnInit { + private courseExerciseService = inject(CourseExerciseService); + private alertService = inject(AlertService); + private participationService = inject(ParticipationService); + readonly FeatureToggle = FeatureToggle; - @Input() - smallButtons: boolean; - @Input() - exercise: Exercise; - @Output() - practiceModeStarted = new EventEmitter(); + @Input() smallButtons: boolean; + @Input() exercise: Exercise; + @Output() practiceModeStarted = new EventEmitter(); startingPracticeMode = false; gradedStudentParticipation?: StudentParticipation; @@ -31,12 +38,6 @@ export class StartPracticeModeButtonComponent implements OnInit { // Icons faRedo = faRedo; - constructor( - private courseExerciseService: CourseExerciseService, - private alertService: AlertService, - private participationService: ParticipationService, - ) {} - ngOnInit() { this.gradedStudentParticipation = this.participationService.getSpecificStudentParticipation(this.exercise.studentParticipations ?? [], false); } diff --git a/src/main/webapp/app/shared/confirm-entity-name/confirm-entity-name.component.ts b/src/main/webapp/app/shared/confirm-entity-name/confirm-entity-name.component.ts index deaad28cc270..ab3606332f75 100644 --- a/src/main/webapp/app/shared/confirm-entity-name/confirm-entity-name.component.ts +++ b/src/main/webapp/app/shared/confirm-entity-name/confirm-entity-name.component.ts @@ -1,6 +1,19 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormControl, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator, Validators } from '@angular/forms'; +import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; +import { + ControlValueAccessor, + FormBuilder, + FormControl, + FormsModule, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ReactiveFormsModule, + ValidationErrors, + Validator, + Validators, +} from '@angular/forms'; import { Subscription } from 'rxjs'; +import { NgClass } from '@angular/common'; +import { TranslateDirective } from '../language/translate.directive'; @Component({ selector: 'jhi-confirm-entity-name', @@ -18,8 +31,11 @@ import { Subscription } from 'rxjs'; useExisting: ConfirmEntityNameComponent, }, ], + imports: [NgClass, TranslateDirective, FormsModule, ReactiveFormsModule], }) export class ConfirmEntityNameComponent implements OnInit, OnDestroy, ControlValueAccessor, Validator { + private fb = inject(FormBuilder); + @Input() warningTextColor: string; @Input() confirmationText: string; @@ -35,8 +51,6 @@ export class ConfirmEntityNameComponent implements OnInit, OnDestroy, ControlVal control: FormControl; - constructor(private fb: FormBuilder) {} - onTouched = () => {}; private currentEntityName: string; diff --git a/src/main/webapp/app/shared/confirm-icon/confirm-icon.component.ts b/src/main/webapp/app/shared/confirm-icon/confirm-icon.component.ts index 99c7cff72c06..c49207839371 100644 --- a/src/main/webapp/app/shared/confirm-icon/confirm-icon.component.ts +++ b/src/main/webapp/app/shared/confirm-icon/confirm-icon.component.ts @@ -1,10 +1,14 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { IconProp, SizeProp } from '@fortawesome/fontawesome-svg-core'; import { faCheck, faTrash } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { NgClass } from '@angular/common'; @Component({ selector: 'jhi-confirm-icon', templateUrl: './confirm-icon.component.html', + imports: [FaIconComponent, NgbTooltip, NgClass], }) export class ConfirmIconComponent { @Input() initialIcon = faTrash; diff --git a/src/main/webapp/app/shared/confirm-icon/confirm-icon.module.ts b/src/main/webapp/app/shared/confirm-icon/confirm-icon.module.ts index be7452db9232..eca910d538ca 100644 --- a/src/main/webapp/app/shared/confirm-icon/confirm-icon.module.ts +++ b/src/main/webapp/app/shared/confirm-icon/confirm-icon.module.ts @@ -4,8 +4,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ConfirmIconComponent } from 'app/shared/confirm-icon/confirm-icon.component'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [ConfirmIconComponent], + imports: [ArtemisSharedModule, ConfirmIconComponent], exports: [ConfirmIconComponent], }) export class ArtemisConfirmIconModule {} diff --git a/src/main/webapp/app/shared/connection-status/connection-status.component.ts b/src/main/webapp/app/shared/connection-status/connection-status.component.ts index 315cbfcb3254..b29faab0f34a 100644 --- a/src/main/webapp/app/shared/connection-status/connection-status.component.ts +++ b/src/main/webapp/app/shared/connection-status/connection-status.component.ts @@ -1,14 +1,20 @@ -import { Component, ContentChild, ElementRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, ContentChild, ElementRef, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { faCircle, faExclamation, faTowerBroadcast } from '@fortawesome/free-solid-svg-icons'; import { Subscription } from 'rxjs'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; +import { NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from '../language/translate.directive'; @Component({ selector: 'jhi-connection-status', templateUrl: './connection-status.component.html', styleUrls: ['./connection-status.component.scss'], + imports: [NgClass, FaIconComponent, TranslateDirective], }) export class JhiConnectionStatusComponent implements OnInit, OnDestroy { + private websocketService = inject(JhiWebsocketService); + @ContentChild('innerContent', { static: false }) innerContent: ElementRef; @Input() isExamMode = false; disconnected = true; @@ -19,8 +25,6 @@ export class JhiConnectionStatusComponent implements OnInit, OnDestroy { readonly faTowerBroadcast = faTowerBroadcast; readonly faExclamation = faExclamation; - constructor(private websocketService: JhiWebsocketService) {} - ngOnInit() { // listen to connect / disconnect events this.websocketStatusSubscription = this.websocketService.connectionState.subscribe((status) => { diff --git a/src/main/webapp/app/shared/connection-warning/connection-warning.component.ts b/src/main/webapp/app/shared/connection-warning/connection-warning.component.ts index ce769ffe9e55..eae68db19f00 100644 --- a/src/main/webapp/app/shared/connection-warning/connection-warning.component.ts +++ b/src/main/webapp/app/shared/connection-warning/connection-warning.component.ts @@ -1,16 +1,24 @@ -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { faExclamationCircle, faWifi } from '@fortawesome/free-solid-svg-icons'; import { Subscription, filter } from 'rxjs'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'; import { NavigationEnd, Router } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from '../language/translate.directive'; +import { CloseCircleComponent } from '../close-circle/close-circle.component'; +import { NgClass } from '@angular/common'; @Component({ selector: 'jhi-connection-warning', templateUrl: './connection-warning.component.html', styleUrls: ['./connection-warning.component.scss'], + imports: [FaIconComponent, TranslateDirective, CloseCircleComponent, NgClass, NgbPopover], }) export class JhiConnectionWarningComponent implements OnInit, OnDestroy { + private websocketService = inject(JhiWebsocketService); + private router = inject(Router); + @ViewChild('popover') popover: NgbPopover; disconnected = false; @@ -23,10 +31,9 @@ export class JhiConnectionWarningComponent implements OnInit, OnDestroy { faExclamationCircle = faExclamationCircle; faWifi = faWifi; - constructor( - private websocketService: JhiWebsocketService, - private router: Router, - ) { + constructor() { + const router = this.router; + this.routerSubscription = router.events .pipe(filter((event) => event instanceof NavigationEnd)) .subscribe((event: NavigationEnd) => (this.isOnExamParticipationPage = !!event.url.match('^/courses/\\d+/exams/\\d+'))); diff --git a/src/main/webapp/app/shared/consistency-check/consistency-check.component.ts b/src/main/webapp/app/shared/consistency-check/consistency-check.component.ts index cfa9cf0ee405..fd5e650bfcb1 100644 --- a/src/main/webapp/app/shared/consistency-check/consistency-check.component.ts +++ b/src/main/webapp/app/shared/consistency-check/consistency-check.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ConsistencyCheckService } from 'app/shared/consistency-check/consistency-check.service'; import { AlertService } from 'app/core/util/alert.service'; @@ -6,12 +6,20 @@ import { ConsistencyCheckError } from 'app/entities/consistency-check-result.mod import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { getCourseId } from 'app/entities/exercise.model'; import { faCheck, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from '../language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { RouterLink } from '@angular/router'; @Component({ selector: 'jhi-consistency-check', templateUrl: './consistency-check.component.html', + imports: [TranslateDirective, FaIconComponent, RouterLink], }) export class ConsistencyCheckComponent implements OnInit { + private activeModal = inject(NgbActiveModal); + private consistencyCheckService = inject(ConsistencyCheckService); + private alertService = inject(AlertService); + @Input() exercisesToCheck: ProgrammingExercise[]; inconsistencies: ConsistencyCheckError[] = []; @@ -21,12 +29,6 @@ export class ConsistencyCheckComponent implements OnInit { faTimes = faTimes; faCheck = faCheck; - constructor( - private activeModal: NgbActiveModal, - private consistencyCheckService: ConsistencyCheckService, - private alertService: AlertService, - ) {} - ngOnInit(): void { this.isLoading = true; let exercisesRemaining = this.exercisesToCheck.length; diff --git a/src/main/webapp/app/shared/consistency-check/consistency-check.service.ts b/src/main/webapp/app/shared/consistency-check/consistency-check.service.ts index 856f74da4420..f815d44ebbcf 100644 --- a/src/main/webapp/app/shared/consistency-check/consistency-check.service.ts +++ b/src/main/webapp/app/shared/consistency-check/consistency-check.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { ConsistencyCheckError } from 'app/entities/consistency-check-result.model'; @@ -6,9 +6,9 @@ import { ConsistencyCheckError } from 'app/entities/consistency-check-result.mod providedIn: 'root', }) export class ConsistencyCheckService { - private readonly RESOURCE_URL = 'api/programming-exercises'; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + private readonly RESOURCE_URL = 'api/programming-exercises'; /** * Request consistency checks for a given programming exercise diff --git a/src/main/webapp/app/shared/course-group/course-group.component.ts b/src/main/webapp/app/shared/course-group/course-group.component.ts index dfd4a21a995a..89de3bd26ca6 100644 --- a/src/main/webapp/app/shared/course-group/course-group.component.ts +++ b/src/main/webapp/app/shared/course-group/course-group.component.ts @@ -11,6 +11,13 @@ import { download, generateCsv, mkConfig } from 'export-to-csv'; import { faDownload, faUserSlash } from '@fortawesome/free-solid-svg-icons'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { EMAIL_KEY, NAME_KEY, REGISTRATION_NUMBER_KEY, USERNAME_KEY } from 'app/shared/export/export-constants'; +import { UsersImportButtonComponent } from '../user-import/users-import-button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from '../language/translate.directive'; +import { NgxDatatableModule } from '@siemens/ngx-datatable'; +import { RouterLink } from '@angular/router'; +import { ProfilePictureComponent } from '../profile-picture/profile-picture.component'; +import { DeleteButtonDirective } from '../delete-dialog/delete-button.directive'; const cssClasses = { alreadyMember: 'already-member', @@ -29,36 +36,25 @@ export type GroupUserInformationRow = { templateUrl: './course-group.component.html', styleUrls: ['./course-group.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [UsersImportButtonComponent, FaIconComponent, TranslateDirective, DataTableComponent, NgxDatatableModule, RouterLink, ProfilePictureComponent, DeleteButtonDirective], }) export class CourseGroupComponent implements OnDestroy { @ViewChild(DataTableComponent) dataTable: DataTableComponent; - @Input() - allGroupUsers: User[] = []; - @Input() - isLoadingAllGroupUsers = false; - @Input() - isAdmin = false; - @Input() - course: Course; - @Input() - tutorialGroup: TutorialGroup | undefined = undefined; - @Input() - courseGroup: CourseGroup; - @Input() - exportFileName: string; + @Input() allGroupUsers: User[] = []; + @Input() isLoadingAllGroupUsers = false; + @Input() isAdmin = false; + @Input() course: Course; + @Input() tutorialGroup: TutorialGroup | undefined = undefined; + @Input() courseGroup: CourseGroup; + @Input() exportFileName: string; - @Input() - userSearch: (loginOrName: string) => Observable> = () => of(new HttpResponse({ body: [] })); - @Input() - addUserToGroup: (login: string) => Observable> = () => of(new HttpResponse()); - @Input() - removeUserFromGroup: (login: string) => Observable> = () => of(new HttpResponse()); - @Input() - handleUsersSizeChange: (filteredUsersSize: number) => void = () => {}; + @Input() userSearch: (loginOrName: string) => Observable> = () => of(new HttpResponse({ body: [] })); + @Input() addUserToGroup: (login: string) => Observable> = () => of(new HttpResponse()); + @Input() removeUserFromGroup: (login: string) => Observable> = () => of(new HttpResponse()); + @Input() handleUsersSizeChange: (filteredUsersSize: number) => void = () => {}; - @Output() - importFinish: EventEmitter = new EventEmitter(); + @Output() importFinish: EventEmitter = new EventEmitter(); readonly ActionType = ActionType; diff --git a/src/main/webapp/app/shared/course-group/course-group.module.ts b/src/main/webapp/app/shared/course-group/course-group.module.ts index 0c145b7ad00c..04b531f5a7d7 100644 --- a/src/main/webapp/app/shared/course-group/course-group.module.ts +++ b/src/main/webapp/app/shared/course-group/course-group.module.ts @@ -8,8 +8,7 @@ import { RouterModule } from '@angular/router'; import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; @NgModule({ - imports: [ArtemisDataTableModule, UserImportModule, NgxDatatableModule, ArtemisSharedModule, RouterModule, ProfilePictureComponent], - declarations: [CourseGroupComponent], + imports: [ArtemisDataTableModule, UserImportModule, NgxDatatableModule, ArtemisSharedModule, RouterModule, ProfilePictureComponent, CourseGroupComponent], exports: [CourseGroupComponent], }) export class ArtemisCourseGroupModule {} diff --git a/src/main/webapp/app/shared/course-users-selector/course-users-selector.component.ts b/src/main/webapp/app/shared/course-users-selector/course-users-selector.component.ts index ad8fc16d851f..5b206edda6d3 100644 --- a/src/main/webapp/app/shared/course-users-selector/course-users-selector.component.ts +++ b/src/main/webapp/app/shared/course-users-selector/course-users-selector.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, ElementRef, HostBinding, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectorRef, Component, ElementRef, HostBinding, Input, OnDestroy, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Observable, OperatorFunction, Subject, catchError, map, of } from 'rxjs'; import { CourseManagementService } from 'app/course/manage/course-management.service'; @@ -6,6 +6,10 @@ import { debounceTime, distinctUntilChanged, filter, switchMap, takeUntil, tap } import { User, UserPublicInfoDTO } from 'app/core/user/user.model'; import { NgbTypeahead, NgbTypeaheadSelectItemEvent } from '@ng-bootstrap/ng-bootstrap'; import { faX } from '@fortawesome/free-solid-svg-icons'; +import { ProfilePictureComponent } from '../profile-picture/profile-picture.component'; +import { TranslateDirective } from '../language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; let selectorId = 0; @@ -33,8 +37,12 @@ export type SearchRoleGroup = 'tutors' | 'students' | 'instructors'; }, ], encapsulation: ViewEncapsulation.None, + imports: [NgbTypeahead, ProfilePictureComponent, TranslateDirective, FaIconComponent, ArtemisTranslatePipe], }) export class CourseUsersSelectorComponent implements ControlValueAccessor, OnInit, OnDestroy { + private courseManagementService = inject(CourseManagementService); + private cdr = inject(ChangeDetectorRef); + @HostBinding('class.course-users-selector') hostClass = true; private ngUnsubscribe = new Subject(); @@ -68,11 +76,6 @@ export class CourseUsersSelectorComponent implements ControlValueAccessor, OnIni isSearching = false; searchFailed = false; - constructor( - private courseManagementService: CourseManagementService, - private cdr: ChangeDetectorRef, - ) {} - ngOnInit(): void { if (this.rolesToAllowSearchingIn.includes('students')) { this.searchStudents = true; diff --git a/src/main/webapp/app/shared/course-users-selector/course-users-selector.module.ts b/src/main/webapp/app/shared/course-users-selector/course-users-selector.module.ts index 8c47180607d0..82e2cf35a168 100644 --- a/src/main/webapp/app/shared/course-users-selector/course-users-selector.module.ts +++ b/src/main/webapp/app/shared/course-users-selector/course-users-selector.module.ts @@ -7,8 +7,7 @@ import { CourseUsersSelectorComponent } from 'app/shared/course-users-selector/c import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; @NgModule({ - imports: [CommonModule, FormsModule, ReactiveFormsModule, ArtemisSharedModule, ArtemisSharedComponentModule, ProfilePictureComponent], + imports: [CommonModule, FormsModule, ReactiveFormsModule, ArtemisSharedModule, ArtemisSharedComponentModule, ProfilePictureComponent, CourseUsersSelectorComponent], exports: [CourseUsersSelectorComponent], - declarations: [CourseUsersSelectorComponent], }) export class CourseUsersSelectorModule {} diff --git a/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.component.ts b/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.component.ts index da1cc727af85..1b47574ca74d 100644 --- a/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.component.ts +++ b/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { TutorLeaderboardElement } from 'app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.model'; import { Course } from 'app/entities/course.model'; import { Exercise, getCourseFromExercise } from 'app/entities/exercise.model'; @@ -6,12 +6,23 @@ import { AccountService } from 'app/core/auth/account.service'; import { SortService } from 'app/shared/service/sort.service'; import { Exam } from 'app/entities/exam/exam.model'; import { faExclamationTriangle, faSort } from '@fortawesome/free-solid-svg-icons'; +import { SortDirective } from '../../sort/sort.directive'; +import { SortByDirective } from '../../sort/sort-by.directive'; +import { TranslateDirective } from '../../language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { RouterLink } from '@angular/router'; +import { ArtemisTranslatePipe } from '../../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutor-leaderboard', templateUrl: './tutor-leaderboard.component.html', + imports: [SortDirective, SortByDirective, TranslateDirective, FaIconComponent, NgbTooltip, RouterLink, ArtemisTranslatePipe], }) export class TutorLeaderboardComponent implements OnInit { + private accountService = inject(AccountService); + private sortService = inject(SortService); + @Input() public tutorsData: TutorLeaderboardElement[] = []; @Input() public course?: Course; @Input() public exercise?: Exercise; @@ -26,11 +37,6 @@ export class TutorLeaderboardComponent implements OnInit { faSort = faSort; faExclamationTriangle = faExclamationTriangle; - constructor( - private accountService: AccountService, - private sortService: SortService, - ) {} - /** * Life cycle hook called by Angular to indicate that Angular is done creating the component */ diff --git a/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.model.ts b/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.model.ts index b1879e6a1565..703227c5c509 100644 --- a/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.model.ts +++ b/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.model.ts @@ -13,6 +13,4 @@ export class TutorLeaderboardElement { public averageRating = 0; public numberOfTutorRatings = 0; public hasIssuesWithPerformance = false; - - constructor() {} } diff --git a/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.module.ts b/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.module.ts index 2ae12d1ef528..e916a735f163 100644 --- a/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.module.ts +++ b/src/main/webapp/app/shared/dashboards/tutor-leaderboard/tutor-leaderboard.module.ts @@ -4,8 +4,7 @@ import { TutorLeaderboardComponent } from 'app/shared/dashboards/tutor-leaderboa import { ArtemisSharedModule } from 'app/shared/shared.module'; @NgModule({ - imports: [ArtemisSharedModule, RouterModule.forChild([])], - declarations: [TutorLeaderboardComponent], + imports: [ArtemisSharedModule, RouterModule.forChild([]), TutorLeaderboardComponent], exports: [TutorLeaderboardComponent], }) export class ArtemisTutorLeaderboardModule {} diff --git a/src/main/webapp/app/shared/dashboards/tutor-participation-graph/progress-bar/progress-bar.component.ts b/src/main/webapp/app/shared/dashboards/tutor-participation-graph/progress-bar/progress-bar.component.ts index 1da8f8762db0..1ef2547dff79 100644 --- a/src/main/webapp/app/shared/dashboards/tutor-participation-graph/progress-bar/progress-bar.component.ts +++ b/src/main/webapp/app/shared/dashboards/tutor-participation-graph/progress-bar/progress-bar.component.ts @@ -1,14 +1,20 @@ -import { ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; +import { ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, SimpleChanges, inject } from '@angular/core'; import { round } from 'app/shared/util/utils'; import { Theme, ThemeService } from 'app/core/theme/theme.service'; import { Subscription } from 'rxjs'; import { toObservable } from '@angular/core/rxjs-interop'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { NgClass } from '@angular/common'; @Component({ selector: 'jhi-progress-bar', templateUrl: './progress-bar.component.html', + imports: [NgbTooltip, NgClass], }) export class ProgressBarComponent implements OnChanges, OnDestroy { + private themeService = inject(ThemeService); + private ref = inject(ChangeDetectorRef); + @Input() public tooltip: string; @Input() public percentage: number; @Input() public numerator: number; @@ -18,10 +24,7 @@ export class ProgressBarComponent implements OnChanges, OnDestroy { backgroundColorClass: string; themeSubscription: Subscription; - constructor( - private themeService: ThemeService, - private ref: ChangeDetectorRef, - ) { + constructor() { this.themeSubscription = toObservable(this.themeService.currentTheme).subscribe(() => { this.chooseProgressBarTextColor(); diff --git a/src/main/webapp/app/shared/dashboards/tutor-participation-graph/tutor-participation-graph.component.ts b/src/main/webapp/app/shared/dashboards/tutor-participation-graph/tutor-participation-graph.component.ts index e1da8a6b0498..f287c64dbf9b 100644 --- a/src/main/webapp/app/shared/dashboards/tutor-participation-graph/tutor-participation-graph.component.ts +++ b/src/main/webapp/app/shared/dashboards/tutor-participation-graph/tutor-participation-graph.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewEncapsulation } from '@angular/core'; +import { Component, Input, OnChanges, OnInit, SimpleChanges, ViewEncapsulation, inject } from '@angular/core'; import { Router } from '@angular/router'; import { get } from 'lodash-es'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; @@ -6,14 +6,22 @@ import { TutorParticipation, TutorParticipationStatus } from 'app/entities/parti import { DueDateStat } from 'app/course/dashboards/due-date-stat.model'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { faBook, faChalkboardTeacher } from '@fortawesome/free-solid-svg-icons'; +import { NgClass } from '@angular/common'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ProgressBarComponent } from './progress-bar/progress-bar.component'; +import { ArtemisTranslatePipe } from '../../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutor-participation-graph', templateUrl: './tutor-participation-graph.component.html', styleUrls: ['./tutor-participation-graph.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [NgClass, NgbTooltip, FaIconComponent, ProgressBarComponent, ArtemisTranslatePipe], }) export class TutorParticipationGraphComponent implements OnInit, OnChanges { + private router = inject(Router); + @Input() public tutorParticipation: TutorParticipation; @Input() public numberOfSubmissions?: DueDateStat; @Input() public totalNumberOfAssessments?: DueDateStat; @@ -45,8 +53,6 @@ export class TutorParticipationGraphComponent implements OnInit, OnChanges { faBook = faBook; faChalkboardTeacher = faChalkboardTeacher; - constructor(private router: Router) {} - /** * Life cycle hook called by Angular to indicate that Angular is done creating the component */ diff --git a/src/main/webapp/app/shared/dashboards/tutor-participation-graph/tutor-participation-graph.module.ts b/src/main/webapp/app/shared/dashboards/tutor-participation-graph/tutor-participation-graph.module.ts index 924d1309a53e..2d89e4d91c0a 100644 --- a/src/main/webapp/app/shared/dashboards/tutor-participation-graph/tutor-participation-graph.module.ts +++ b/src/main/webapp/app/shared/dashboards/tutor-participation-graph/tutor-participation-graph.module.ts @@ -4,8 +4,7 @@ import { TutorParticipationGraphComponent } from 'app/shared/dashboards/tutor-pa import { ArtemisSharedModule } from 'app/shared/shared.module'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [ProgressBarComponent, TutorParticipationGraphComponent], + imports: [ArtemisSharedModule, ProgressBarComponent, TutorParticipationGraphComponent], exports: [TutorParticipationGraphComponent, ProgressBarComponent], }) export class ArtemisTutorParticipationGraphModule {} diff --git a/src/main/webapp/app/shared/data-table/data-table.component.ts b/src/main/webapp/app/shared/data-table/data-table.component.ts index 60c93ef5d313..33370b27379c 100644 --- a/src/main/webapp/app/shared/data-table/data-table.component.ts +++ b/src/main/webapp/app/shared/data-table/data-table.component.ts @@ -1,4 +1,18 @@ -import { Component, ContentChild, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; +import { + Component, + ContentChild, + ElementRef, + EventEmitter, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, + TemplateRef, + ViewChild, + ViewEncapsulation, + inject, +} from '@angular/core'; import { debounceTime, distinctUntilChanged, map, tap } from 'rxjs/operators'; import { Observable } from 'rxjs'; import { ColumnMode, SortType } from '@siemens/ngx-datatable'; @@ -7,6 +21,11 @@ import { BaseEntity, StringBaseEntity } from 'app/shared/model/base-entity'; import { LocalStorageService } from 'ngx-webstorage'; import { SortService } from 'app/shared/service/sort.service'; import { faCircleNotch, faSort, faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons'; +import { NgbDropdown, NgbDropdownButtonItem, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from '../language/translate.directive'; +import { NgTemplateOutlet } from '@angular/common'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; /** * Enum for ascending and descending order. @@ -46,8 +65,23 @@ type PagingValue = number | 'all'; templateUrl: './data-table.component.html', styleUrls: ['data-table.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + NgbDropdown, + NgbDropdownToggle, + NgbDropdownMenu, + NgbDropdownButtonItem, + NgbDropdownItem, + NgbTypeahead, + FaIconComponent, + TranslateDirective, + NgTemplateOutlet, + ArtemisTranslatePipe, + ], }) export class DataTableComponent implements OnInit, OnChanges { + private sortService = inject(SortService); + private localStorage = inject(LocalStorageService); + /** * @property templateRef Ref to the content child of this component (which is ngx-datatable) */ @@ -145,10 +179,7 @@ export class DataTableComponent implements OnInit, OnChanges { // Icons faCircleNotch = faCircleNotch; - constructor( - private sortService: SortService, - private localStorage: LocalStorageService, - ) { + constructor() { this.entities = []; this.entityCriteria = { textSearch: [], diff --git a/src/main/webapp/app/shared/data-table/data-table.module.ts b/src/main/webapp/app/shared/data-table/data-table.module.ts index 1740b6af5d49..067d3c6f1b20 100644 --- a/src/main/webapp/app/shared/data-table/data-table.module.ts +++ b/src/main/webapp/app/shared/data-table/data-table.module.ts @@ -4,8 +4,7 @@ import { NgxDatatableModule } from '@siemens/ngx-datatable'; import { DataTableComponent } from './data-table.component'; @NgModule({ - imports: [ArtemisSharedModule, NgxDatatableModule], - declarations: [DataTableComponent], + imports: [ArtemisSharedModule, NgxDatatableModule, DataTableComponent], exports: [DataTableComponent], }) export class ArtemisDataTableModule {} diff --git a/src/main/webapp/app/shared/date-time-picker/date-time-picker.component.ts b/src/main/webapp/app/shared/date-time-picker/date-time-picker.component.ts index 78d1f46b9a11..8a685019ece1 100644 --- a/src/main/webapp/app/shared/date-time-picker/date-time-picker.component.ts +++ b/src/main/webapp/app/shared/date-time-picker/date-time-picker.component.ts @@ -1,7 +1,13 @@ import { Component, ViewChild, computed, forwardRef, input, model, output, signal } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgModel } from '@angular/forms'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR, NgModel } from '@angular/forms'; import { faCalendarAlt, faCircleXmark, faClock, faGlobe, faQuestionCircle, faTriangleExclamation } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; +import { FaIconComponent, FaStackComponent, FaStackItemSizeDirective } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { OwlDateTimeModule } from '@danielmoncada/angular-datetime-picker'; +import { NgClass, NgTemplateOutlet } from '@angular/common'; +import { TranslateDirective } from '../language/translate.directive'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; export enum DateTimePickerType { CALENDAR, @@ -20,6 +26,18 @@ export enum DateTimePickerType { useExisting: forwardRef(() => FormDateTimePickerComponent), }, ], + imports: [ + FaStackComponent, + NgbTooltip, + FaIconComponent, + FaStackItemSizeDirective, + FormsModule, + OwlDateTimeModule, + NgClass, + NgTemplateOutlet, + TranslateDirective, + ArtemisTranslatePipe, + ], }) export class FormDateTimePickerComponent implements ControlValueAccessor { protected readonly faCalendarAlt = faCalendarAlt; diff --git a/src/main/webapp/app/shared/date-time-picker/date-time-picker.module.ts b/src/main/webapp/app/shared/date-time-picker/date-time-picker.module.ts index 682a28393f44..8c4b84c7b3d9 100644 --- a/src/main/webapp/app/shared/date-time-picker/date-time-picker.module.ts +++ b/src/main/webapp/app/shared/date-time-picker/date-time-picker.module.ts @@ -15,9 +15,17 @@ export const MY_NATIVE_FORMATS = { monthYearA11yLabel: { year: 'numeric', month: 'long' }, }; @NgModule({ - imports: [CommonModule, FormsModule, OwlDateTimeModule, OwlNativeDateTimeModule, ReactiveFormsModule, ArtemisSharedModule, ArtemisSharedComponentModule], + imports: [ + CommonModule, + FormsModule, + OwlDateTimeModule, + OwlNativeDateTimeModule, + ReactiveFormsModule, + ArtemisSharedModule, + ArtemisSharedComponentModule, + FormDateTimePickerComponent, + ], exports: [FormDateTimePickerComponent], - declarations: [FormDateTimePickerComponent], providers: [{ provide: OWL_DATE_TIME_FORMATS, useValue: MY_NATIVE_FORMATS }], }) export class FormDateTimePickerModule {} diff --git a/src/main/webapp/app/shared/delete-dialog/delete-button.directive.ts b/src/main/webapp/app/shared/delete-dialog/delete-button.directive.ts index 6fe508245dc8..adcd403a4243 100644 --- a/src/main/webapp/app/shared/delete-dialog/delete-button.directive.ts +++ b/src/main/webapp/app/shared/delete-dialog/delete-button.directive.ts @@ -1,5 +1,5 @@ import { DeleteDialogService } from 'app/shared/delete-dialog/delete-dialog.service'; -import { Directive, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, Renderer2 } from '@angular/core'; +import { Directive, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, Renderer2, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ActionType, DeleteDialogData, EntitySummary } from 'app/shared/delete-dialog/delete-dialog.model'; import { Observable } from 'rxjs'; @@ -7,6 +7,11 @@ import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; @Directive({ selector: '[jhiDeleteButton]' }) export class DeleteButtonDirective implements OnInit { + private deleteDialogService = inject(DeleteDialogService); + private renderer = inject(Renderer2); + private elementRef = inject(ElementRef); + private translateService = inject(TranslateService); + @Input() entityTitle?: string; @Input() deleteQuestion: string; @Input() entitySummaryTitle?: string; @@ -26,13 +31,6 @@ export class DeleteButtonDirective implements OnInit { deleteTextSpan: HTMLElement; - constructor( - private deleteDialogService: DeleteDialogService, - private renderer: Renderer2, - private elementRef: ElementRef, - private translateService: TranslateService, - ) {} - /** * This method appends classes and type property to the button on which directive was used, additionally adds a span tag with delete text. * We can't use component, as Angular would wrap it in its own tag and this will break button grouping that we are using for other buttons. diff --git a/src/main/webapp/app/shared/delete-dialog/delete-dialog.component.ts b/src/main/webapp/app/shared/delete-dialog/delete-dialog.component.ts index 0a85965d0b38..f7e1756c0ca8 100644 --- a/src/main/webapp/app/shared/delete-dialog/delete-dialog.component.ts +++ b/src/main/webapp/app/shared/delete-dialog/delete-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; +import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { mapValues } from 'lodash-es'; import { ActionType, EntitySummary } from 'app/shared/delete-dialog/delete-dialog.model'; @@ -6,13 +6,22 @@ import { Observable, Subscription } from 'rxjs'; import { AlertService } from 'app/core/util/alert.service'; import { faBan, faCheck, faSpinner, faTimes, faTrash, faUndo } from '@fortawesome/free-solid-svg-icons'; import { ButtonType } from 'app/shared/components/button.component'; -import { NgForm } from '@angular/forms'; +import { FormsModule, NgForm } from '@angular/forms'; +import { TranslateDirective } from '../language/translate.directive'; +import { ConfirmEntityNameComponent } from '../confirm-entity-name/confirm-entity-name.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass } from '@angular/common'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-delete-dialog', templateUrl: './delete-dialog.component.html', + imports: [FormsModule, TranslateDirective, ConfirmEntityNameComponent, FaIconComponent, NgClass, ArtemisTranslatePipe], }) export class DeleteDialogComponent implements OnInit, OnDestroy { + private activeModal = inject(NgbActiveModal); + private alertService = inject(AlertService); + readonly actionTypes = ActionType; private dialogErrorSubscription: Subscription; dialogError: Observable; @@ -47,11 +56,6 @@ export class DeleteDialogComponent implements OnInit, OnDestroy { faUndo = faUndo; warningTextColor: string; - constructor( - private activeModal: NgbActiveModal, - private alertService: AlertService, - ) {} - /** * Life cycle hook called by Angular to indicate that Angular is done creating the component */ diff --git a/src/main/webapp/app/shared/delete-dialog/delete-dialog.service.ts b/src/main/webapp/app/shared/delete-dialog/delete-dialog.service.ts index 02ab549f82a8..1ee27173cf78 100644 --- a/src/main/webapp/app/shared/delete-dialog/delete-dialog.service.ts +++ b/src/main/webapp/app/shared/delete-dialog/delete-dialog.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; import { DeleteDialogData, EntitySummary } from 'app/shared/delete-dialog/delete-dialog.model'; @@ -8,12 +8,10 @@ import { HttpErrorResponse } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class DeleteDialogService { - modalRef: NgbModalRef | null; + private modalService = inject(NgbModal); + alertService = inject(AlertService); - constructor( - private modalService: NgbModal, - public alertService: AlertService, - ) {} + modalRef: NgbModalRef | null; /** * Opens delete dialog diff --git a/src/main/webapp/app/shared/detail-overview-navigation-bar/detail-overview-navigation-bar.component.ts b/src/main/webapp/app/shared/detail-overview-navigation-bar/detail-overview-navigation-bar.component.ts index e9eb25918015..60627b646794 100644 --- a/src/main/webapp/app/shared/detail-overview-navigation-bar/detail-overview-navigation-bar.component.ts +++ b/src/main/webapp/app/shared/detail-overview-navigation-bar/detail-overview-navigation-bar.component.ts @@ -1,10 +1,12 @@ import { AfterViewInit, Component, HostListener, Input } from '@angular/core'; import { updateHeaderHeight } from 'app/shared/util/navbar.util'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-detail-overview-navigation-bar', templateUrl: './detail-overview-navigation-bar.component.html', styleUrls: ['./detail-overview-navigation-bar.scss'], + imports: [ArtemisTranslatePipe], }) export class DetailOverviewNavigationBarComponent implements AfterViewInit { @Input() diff --git a/src/main/webapp/app/shared/difficulty-level/difficulty-level.component.ts b/src/main/webapp/app/shared/difficulty-level/difficulty-level.component.ts index 69af618315e3..b2cd741d3ea2 100644 --- a/src/main/webapp/app/shared/difficulty-level/difficulty-level.component.ts +++ b/src/main/webapp/app/shared/difficulty-level/difficulty-level.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { DifficultyLevel } from 'app/entities/exercise.model'; import { Subscription } from 'rxjs'; @@ -13,16 +13,15 @@ export interface ColoredDifficultyLevel { selector: 'jhi-difficulty-level', templateUrl: './difficulty-level.component.html', styleUrls: ['./difficulty-level.component.scss'], - standalone: true, imports: [ArtemisSharedModule, ArtemisSharedComponentModule], }) export class DifficultyLevelComponent implements OnInit, OnDestroy { + private translateService = inject(TranslateService); + private translateSubscription: Subscription; @Input() difficultyLevel: string; coloredDifficultyLevel: ColoredDifficultyLevel = { label: '', color: [] }; - constructor(private translateService: TranslateService) {} - ngOnInit(): void { this.translateSubscription = this.translateService.onLangChange.subscribe(() => { this.mapDifficultyLevelToColors(this.difficultyLevel); diff --git a/src/main/webapp/app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.ts b/src/main/webapp/app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.ts index aec203a26946..f4cad98c580f 100644 --- a/src/main/webapp/app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.ts +++ b/src/main/webapp/app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.ts @@ -11,14 +11,13 @@ type CategoryFontSize = 'default' | 'small'; selector: 'jhi-custom-exercise-category-badge', templateUrl: './custom-exercise-category-badge.component.html', styleUrls: ['custom-exercise-category-badge.component.scss'], - standalone: true, imports: [CommonModule, FontAwesomeModule], }) export class CustomExerciseCategoryBadgeComponent { protected readonly faTimes = faTimes; @Input({ required: true }) category: ExerciseCategory | FaqCategory; - @Input() displayRemoveButton: boolean = false; + @Input() displayRemoveButton = false; @Input() onClick: () => void = () => {}; @Input() fontSize: CategoryFontSize = 'default'; } diff --git a/src/main/webapp/app/shared/exercise-categories/exercise-categories.component.ts b/src/main/webapp/app/shared/exercise-categories/exercise-categories.component.ts index 065c59b801fc..6088528f014c 100644 --- a/src/main/webapp/app/shared/exercise-categories/exercise-categories.component.ts +++ b/src/main/webapp/app/shared/exercise-categories/exercise-categories.component.ts @@ -2,6 +2,13 @@ import { Component, Input } from '@angular/core'; import { Exercise, IncludedInOverallScore } from 'app/entities/exercise.model'; import dayjs from 'dayjs/esm'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; +import { NgClass, NgStyle } from '@angular/common'; +import { RouterLink } from '@angular/router'; +import { NotReleasedTagComponent } from '../components/not-released-tag.component'; +import { TranslateDirective } from '../language/translate.directive'; +import { IncludedInScoreBadgeComponent } from '../../exercises/shared/exercise-headers/included-in-score-badge.component'; +import { DifficultyBadgeComponent } from '../../exercises/shared/exercise-headers/difficulty-badge.component'; +import { TruncatePipe } from 'app/shared/pipes/truncate.pipe'; interface ShowTagsConfig { notReleased?: boolean; @@ -15,13 +22,14 @@ interface ShowTagsConfig { selector: 'jhi-exercise-categories', templateUrl: './exercise-categories.component.html', styleUrls: ['./exercise-categories.component.scss'], + imports: [NgClass, RouterLink, NotReleasedTagComponent, TranslateDirective, IncludedInScoreBadgeComponent, NgStyle, DifficultyBadgeComponent, TruncatePipe], }) export class ExerciseCategoriesComponent { readonly IncludedInOverallScore = IncludedInOverallScore; readonly dayjs = dayjs; @Input() exercise: Exercise; - @Input() isSmall: boolean = false; + @Input() isSmall = false; @Input() showTags: ShowTagsConfig = { diff --git a/src/main/webapp/app/shared/exercise-categories/exercise-categories.module.ts b/src/main/webapp/app/shared/exercise-categories/exercise-categories.module.ts index f0f8877ffdec..2502f21738c2 100644 --- a/src/main/webapp/app/shared/exercise-categories/exercise-categories.module.ts +++ b/src/main/webapp/app/shared/exercise-categories/exercise-categories.module.ts @@ -6,8 +6,7 @@ import { ExerciseCategoriesComponent } from 'app/shared/exercise-categories/exer import { CustomExerciseCategoryBadgeComponent } from 'app/shared/exercise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component'; @NgModule({ - imports: [ArtemisSharedModule, RouterModule, ArtemisSharedComponentModule, CustomExerciseCategoryBadgeComponent], - declarations: [ExerciseCategoriesComponent], + imports: [ArtemisSharedModule, RouterModule, ArtemisSharedComponentModule, CustomExerciseCategoryBadgeComponent, ExerciseCategoriesComponent], exports: [ExerciseCategoriesComponent], }) export class ExerciseCategoriesModule {} diff --git a/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts b/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts index 8b1bf7c58488..cef21387f1c4 100644 --- a/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts +++ b/src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.ts @@ -1,5 +1,5 @@ -import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'; -import { NgbActiveModal, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; +import { Component, EventEmitter, OnInit, Output, ViewChild, inject } from '@angular/core'; +import { NgbActiveModal, NgbModule, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; import { faBackward, faFilter } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @@ -29,7 +29,6 @@ import { isRangeFilterApplied } from 'app/shared/sidebar/sidebar.helper'; selector: 'jhi-exercise-filter-modal', templateUrl: './exercise-filter-modal.component.html', styleUrls: ['./exercise-filter-modal.component.scss'], - standalone: true, imports: [ FormsModule, ReactiveFormsModule, @@ -38,9 +37,12 @@ import { isRangeFilterApplied } from 'app/shared/sidebar/sidebar.helper'; ArtemisSharedComponentModule, CustomExerciseCategoryBadgeComponent, RangeSliderComponent, + NgbModule, ], }) export class ExerciseFilterModalComponent implements OnInit { + private activeModal = inject(NgbActiveModal); + readonly faFilter = faFilter; readonly faBackward = faBackward; @@ -51,7 +53,7 @@ export class ExerciseFilterModalComponent implements OnInit { selectedCategoryOptions: ExerciseCategoryFilterOption[] = []; selectableCategoryOptions: ExerciseCategoryFilterOption[] = []; - noFiltersAvailable: boolean = false; + noFiltersAvailable = false; focus$ = new Subject(); click$ = new Subject(); @@ -70,8 +72,6 @@ export class ExerciseFilterModalComponent implements OnInit { exerciseFilters?: ExerciseFilterOptions; - constructor(private activeModal: NgbActiveModal) {} - ngOnInit() { this.categoryFilter = this.exerciseFilters?.categoryFilter; this.typeFilter = this.exerciseFilters?.exerciseTypesFilter; diff --git a/src/main/webapp/app/shared/export/export-button.component.ts b/src/main/webapp/app/shared/export/export-button.component.ts index 4d5de6b7d49f..94f8c4f4cd62 100644 --- a/src/main/webapp/app/shared/export/export-button.component.ts +++ b/src/main/webapp/app/shared/export/export-button.component.ts @@ -1,14 +1,18 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { CsvExportOptions, ExportModalComponent } from 'app/shared/export/export-modal.component'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { ButtonComponent } from '../components/button.component'; @Component({ selector: 'jhi-csv-export-button', template: ` `, + imports: [ButtonComponent], }) export class ExportButtonComponent { + private modalService = inject(NgbModal); + ButtonType = ButtonType; ButtonSize = ButtonSize; @@ -19,8 +23,6 @@ export class ExportButtonComponent { @Output() onExport: EventEmitter = new EventEmitter(); - constructor(private modalService: NgbModal) {} - /** * Open up export option modal * @param {Event} event - Mouse Event which invoked the opening diff --git a/src/main/webapp/app/shared/export/export-modal.component.ts b/src/main/webapp/app/shared/export/export-modal.component.ts index d5e35b1594bd..10974e03f5b3 100644 --- a/src/main/webapp/app/shared/export/export-modal.component.ts +++ b/src/main/webapp/app/shared/export/export-modal.component.ts @@ -1,7 +1,12 @@ -import { Component, OnInit } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { Component, OnInit, inject } from '@angular/core'; +import { NgbActiveModal, NgbNav, NgbNavContent, NgbNavItem, NgbNavLink, NgbNavLinkBase, NgbNavOutlet } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { faBan, faDownload } from '@fortawesome/free-solid-svg-icons'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from '../language/translate.directive'; +import { KeyValuePipe, NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; export enum CsvFieldSeparator { TAB = '\t', @@ -33,8 +38,25 @@ export interface CsvExportOptions { selector: 'jhi-csv-export-modal', templateUrl: './export-modal.component.html', styleUrls: ['./export-modal.component.scss'], + imports: [ + FormsModule, + TranslateDirective, + NgbNav, + NgbNavItem, + NgbNavLink, + NgbNavLinkBase, + NgbNavContent, + NgClass, + NgbNavOutlet, + FaIconComponent, + KeyValuePipe, + ArtemisTranslatePipe, + ], }) export class ExportModalComponent implements OnInit { + private activeModal = inject(NgbActiveModal); + private translateService = inject(TranslateService); + readonly CsvFieldSeparator = CsvFieldSeparator; readonly CsvQuoteStrings = CsvQuoteStrings; readonly CsvDecimalSeparator = CsvDecimalSeparator; @@ -46,11 +68,6 @@ export class ExportModalComponent implements OnInit { faBan = faBan; faDownload = faDownload; - constructor( - private activeModal: NgbActiveModal, - private translateService: TranslateService, - ) {} - ngOnInit(): void { // set default csv export options based on the current language switch (this.translateService.currentLang) { diff --git a/src/main/webapp/app/shared/export/export.module.ts b/src/main/webapp/app/shared/export/export.module.ts index 19f79a14389e..939b5356f0cf 100644 --- a/src/main/webapp/app/shared/export/export.module.ts +++ b/src/main/webapp/app/shared/export/export.module.ts @@ -5,8 +5,7 @@ import { ArtemisSharedComponentModule } from 'app/shared/components/shared-compo import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @NgModule({ - imports: [ArtemisSharedComponentModule, ArtemisSharedCommonModule], - declarations: [ExportModalComponent, ExportButtonComponent], + imports: [ArtemisSharedComponentModule, ArtemisSharedCommonModule, ExportModalComponent, ExportButtonComponent], exports: [ExportModalComponent, ExportButtonComponent], }) export class ExportModule {} diff --git a/src/main/webapp/app/shared/extension-point/extension-point.directive.ts b/src/main/webapp/app/shared/extension-point/extension-point.directive.ts index ce0fad03beb7..27296cda1632 100644 --- a/src/main/webapp/app/shared/extension-point/extension-point.directive.ts +++ b/src/main/webapp/app/shared/extension-point/extension-point.directive.ts @@ -1,4 +1,4 @@ -import { Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef } from '@angular/core'; +import { Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef, inject } from '@angular/core'; /** * @whatItDoes marks parts of a (parent) template as extendable to allow other (child) components to override them. @@ -27,16 +27,14 @@ import { Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRe */ @Directive({ selector: '[jhiExtensionPoint]' }) export class ExtensionPointDirective implements OnChanges { + private viewContainerRef = inject(ViewContainerRef); + private templateRef = inject>(TemplateRef); + private viewRef: EmbeddedViewRef | undefined = undefined; @Input() public jhiExtensionPoint: TemplateRef | undefined = undefined; @Input() public jhiExtensionPointContext?: any = undefined; - constructor( - private viewContainerRef: ViewContainerRef, - private templateRef: TemplateRef, - ) {} - ngOnChanges(changes: SimpleChanges) { if (changes['jhiExtensionPoint']) { const viewContainerRef = this.viewContainerRef; diff --git a/src/main/webapp/app/shared/feature-toggle/feature-toggle-hide.directive.ts b/src/main/webapp/app/shared/feature-toggle/feature-toggle-hide.directive.ts index e77255d5bca5..9a17d2c0f6b6 100644 --- a/src/main/webapp/app/shared/feature-toggle/feature-toggle-hide.directive.ts +++ b/src/main/webapp/app/shared/feature-toggle/feature-toggle-hide.directive.ts @@ -1,19 +1,17 @@ -import { Directive, HostBinding, Input, OnDestroy, OnInit } from '@angular/core'; +import { Directive, HostBinding, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { FeatureToggle, FeatureToggleService } from 'app/shared/feature-toggle/feature-toggle.service'; import { Subscription } from 'rxjs/internal/Subscription'; -@Directive({ - selector: '[jhiFeatureToggleHide]', -}) +@Directive({ selector: '[jhiFeatureToggleHide]' }) export class FeatureToggleHideDirective implements OnInit, OnDestroy { + private featureToggleService = inject(FeatureToggleService); + @Input('jhiFeatureToggleHide') feature?: FeatureToggle; private featureActive = true; private featureToggleActiveSubscription: Subscription; - constructor(private featureToggleService: FeatureToggleService) {} - ngOnInit() { if (this.feature) { this.featureToggleActiveSubscription = this.featureToggleService.getFeatureToggleActive(this.feature).subscribe((active) => { diff --git a/src/main/webapp/app/shared/feature-toggle/feature-toggle-link.directive.ts b/src/main/webapp/app/shared/feature-toggle/feature-toggle-link.directive.ts index f29b25e1792e..26e8de642d86 100644 --- a/src/main/webapp/app/shared/feature-toggle/feature-toggle-link.directive.ts +++ b/src/main/webapp/app/shared/feature-toggle/feature-toggle-link.directive.ts @@ -1,12 +1,12 @@ -import { Directive, HostBinding, Input, OnDestroy, OnInit } from '@angular/core'; +import { Directive, HostBinding, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { FeatureToggle, FeatureToggleService } from 'app/shared/feature-toggle/feature-toggle.service'; import { tap } from 'rxjs/operators'; import { Subscription } from 'rxjs'; -@Directive({ - selector: '[jhiFeatureToggleLink]', -}) +@Directive({ selector: '[jhiFeatureToggleLink]' }) export class FeatureToggleLinkDirective implements OnInit, OnDestroy { + private featureToggleService = inject(FeatureToggleService); + @Input('jhiFeatureToggleLink') feature: FeatureToggle; /** * This input must be used to overwrite the disabled state given that the feature toggle is inactive. @@ -23,8 +23,6 @@ export class FeatureToggleLinkDirective implements OnInit, OnDestroy { private featureToggleActiveSubscription: Subscription; - constructor(private featureToggleService: FeatureToggleService) {} - /** * Life cycle hook called by Angular to indicate that Angular is done creating the component */ diff --git a/src/main/webapp/app/shared/feature-toggle/feature-toggle.directive.ts b/src/main/webapp/app/shared/feature-toggle/feature-toggle.directive.ts index 3a8d02976f44..dffc86cbe69a 100644 --- a/src/main/webapp/app/shared/feature-toggle/feature-toggle.directive.ts +++ b/src/main/webapp/app/shared/feature-toggle/feature-toggle.directive.ts @@ -1,12 +1,12 @@ -import { Directive, HostBinding, Input, OnDestroy, OnInit } from '@angular/core'; +import { Directive, HostBinding, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { FeatureToggle, FeatureToggleService } from 'app/shared/feature-toggle/feature-toggle.service'; import { tap } from 'rxjs/operators'; import { Subscription } from 'rxjs'; -@Directive({ - selector: '[jhiFeatureToggle]', -}) +@Directive({ selector: '[jhiFeatureToggle]' }) export class FeatureToggleDirective implements OnInit, OnDestroy { + private featureToggleService = inject(FeatureToggleService); + @Input('jhiFeatureToggle') features: FeatureToggle | FeatureToggle[]; /** * This input must be used to overwrite the disabled state given that the feature toggle is inactive. @@ -23,8 +23,6 @@ export class FeatureToggleDirective implements OnInit, OnDestroy { private featureToggleActiveSubscription: Subscription; - constructor(private featureToggleService: FeatureToggleService) {} - /** * Life cycle hook called by Angular to indicate that Angular is done creating the component */ diff --git a/src/main/webapp/app/shared/feature-toggle/feature-toggle.module.ts b/src/main/webapp/app/shared/feature-toggle/feature-toggle.module.ts deleted file mode 100644 index 64b19d937f81..000000000000 --- a/src/main/webapp/app/shared/feature-toggle/feature-toggle.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NgModule } from '@angular/core'; -import { FeatureToggleDirective } from 'app/shared/feature-toggle/feature-toggle.directive'; -import { FeatureToggleLinkDirective } from 'app/shared/feature-toggle/feature-toggle-link.directive'; -import { FeatureToggleHideDirective } from 'app/shared/feature-toggle/feature-toggle-hide.directive'; - -@NgModule({ - declarations: [FeatureToggleDirective, FeatureToggleLinkDirective, FeatureToggleHideDirective], - exports: [FeatureToggleDirective, FeatureToggleLinkDirective, FeatureToggleHideDirective], -}) -export class FeatureToggleModule {} diff --git a/src/main/webapp/app/shared/feature-toggle/feature-toggle.service.ts b/src/main/webapp/app/shared/feature-toggle/feature-toggle.service.ts index 43cc4ce4d6b5..283f60e2d313 100644 --- a/src/main/webapp/app/shared/feature-toggle/feature-toggle.service.ts +++ b/src/main/webapp/app/shared/feature-toggle/feature-toggle.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { distinctUntilChanged, map, tap } from 'rxjs/operators'; @@ -25,14 +25,14 @@ const defaultActiveFeatureState: ActiveFeatureToggles = Object.values(FeatureTog @Injectable({ providedIn: 'root' }) export class FeatureToggleService { + private websocketService = inject(JhiWebsocketService); + private http = inject(HttpClient); + private readonly TOPIC = `/topic/management/feature-toggles`; private subject: BehaviorSubject; private subscriptionInitialized = false; - constructor( - private websocketService: JhiWebsocketService, - private http: HttpClient, - ) { + constructor() { this.subject = new BehaviorSubject(defaultActiveFeatureState); } diff --git a/src/main/webapp/app/shared/fingerprint/browser-fingerprint.service.ts b/src/main/webapp/app/shared/fingerprint/browser-fingerprint.service.ts index 1cd278a86da9..8088c0b38320 100644 --- a/src/main/webapp/app/shared/fingerprint/browser-fingerprint.service.ts +++ b/src/main/webapp/app/shared/fingerprint/browser-fingerprint.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { LocalStorageService } from 'ngx-webstorage'; import FingerprintJS, { GetResult } from '@fingerprintjs/fingerprintjs'; @@ -6,13 +6,13 @@ import { v4 as uuid } from 'uuid'; @Injectable({ providedIn: 'root' }) export class BrowserFingerprintService { + private localStorage = inject(LocalStorageService); + private readonly BROWSER_INSTANCE_KEY = 'instanceIdentifier'; public fingerprint = new BehaviorSubject(undefined); public instanceIdentifier = new BehaviorSubject(undefined); - constructor(private localStorage: LocalStorageService) {} - public initialize(browserFingerprintsEnabled: boolean | undefined) { // If undefined, still enable it to not break older configurations without the field in profile info if (browserFingerprintsEnabled !== false) { diff --git a/src/main/webapp/app/shared/fireworks/fireworks.module.ts b/src/main/webapp/app/shared/fireworks/fireworks.module.ts deleted file mode 100644 index cfb28a586b87..000000000000 --- a/src/main/webapp/app/shared/fireworks/fireworks.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { CommonModule } from '@angular/common'; -import { NgModule } from '@angular/core'; -import { FireworksComponent } from 'app/shared/fireworks/fireworks.component'; - -@NgModule({ - imports: [CommonModule], - declarations: [FireworksComponent], - exports: [FireworksComponent], -}) -export class FireworksModule {} diff --git a/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.component.ts b/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.component.ts index b7f139bc5182..47ea9fd3f883 100644 --- a/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.component.ts +++ b/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.component.ts @@ -1,12 +1,16 @@ import { AfterViewInit, Component, Input, OnDestroy, OnInit, ViewChild, computed, effect, input, output, signal, viewChild } from '@angular/core'; -import { ControlContainer, NgForm, NgModel } from '@angular/forms'; +import { ControlContainer, FormsModule, NgForm, NgModel } from '@angular/forms'; import { Subject, Subscription } from 'rxjs'; import { ProgrammingExerciseInputField } from 'app/exercises/programming/manage/update/programming-exercise-update.helper'; +import { TranslateDirective } from '../../language/translate.directive'; +import { CustomNotIncludedInValidatorDirective } from '../../validators/custom-not-included-in-validator.directive'; +import { HelpIconComponent } from '../../components/help-icon.component'; @Component({ selector: 'jhi-title-channel-name', templateUrl: './title-channel-name.component.html', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], + imports: [TranslateDirective, FormsModule, CustomNotIncludedInValidatorDirective, HelpIconComponent], }) export class TitleChannelNameComponent implements AfterViewInit, OnDestroy, OnInit { @Input() title?: string; diff --git a/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.module.ts b/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.module.ts index 70849d7bf364..a45e45df2530 100644 --- a/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.module.ts +++ b/src/main/webapp/app/shared/form/title-channel-name/title-channel-name.module.ts @@ -7,8 +7,7 @@ import { CustomNotIncludedInValidatorDirective } from 'app/shared/validators/cus import { TranslateDirective } from 'app/shared/language/translate.directive'; @NgModule({ - imports: [FormsModule, CommonModule, ArtemisSharedComponentModule, CustomNotIncludedInValidatorDirective, TranslateDirective], - declarations: [TitleChannelNameComponent], + imports: [FormsModule, CommonModule, ArtemisSharedComponentModule, CustomNotIncludedInValidatorDirective, TranslateDirective, TitleChannelNameComponent], exports: [TitleChannelNameComponent], }) export class TitleChannelNameModule {} diff --git a/src/main/webapp/app/shared/fullscreen/fullscreen.component.ts b/src/main/webapp/app/shared/fullscreen/fullscreen.component.ts index fdcf2046080b..f81b5459bf10 100644 --- a/src/main/webapp/app/shared/fullscreen/fullscreen.component.ts +++ b/src/main/webapp/app/shared/fullscreen/fullscreen.component.ts @@ -1,13 +1,19 @@ -import { Component, ElementRef, Input } from '@angular/core'; +import { Component, ElementRef, Input, inject } from '@angular/core'; import { faCompress } from '@fortawesome/free-solid-svg-icons'; import { enterFullscreen, exitFullscreen, isFullScreen } from 'app/shared/util/fullscreen.util'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from '../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-fullscreen', templateUrl: './fullscreen.component.html', styleUrls: ['./fullscreen.scss'], + imports: [NgbTooltip, FaIconComponent, ArtemisTranslatePipe], }) export class FullscreenComponent { + private fullScreenWrapper = inject(ElementRef); + @Input() position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' = 'top-right'; @@ -17,8 +23,6 @@ export class FullscreenComponent { // Icons faCompress = faCompress; - constructor(private fullScreenWrapper: ElementRef) {} - /** * check current state and toggle fullscreen */ diff --git a/src/main/webapp/app/shared/fullscreen/fullscreen.module.ts b/src/main/webapp/app/shared/fullscreen/fullscreen.module.ts index db982626257d..29d28ad26f97 100644 --- a/src/main/webapp/app/shared/fullscreen/fullscreen.module.ts +++ b/src/main/webapp/app/shared/fullscreen/fullscreen.module.ts @@ -3,8 +3,7 @@ import { NgModule } from '@angular/core'; import { ArtemisSharedModule } from 'app/shared/shared.module'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [FullscreenComponent], + imports: [ArtemisSharedModule, FullscreenComponent], exports: [FullscreenComponent], }) export class ArtemisFullscreenModule {} diff --git a/src/main/webapp/app/shared/grading-instruction-link-icon/grading-instruction-link-icon.component.ts b/src/main/webapp/app/shared/grading-instruction-link-icon/grading-instruction-link-icon.component.ts index 5702a3ce76c7..3147e1f0d1b6 100644 --- a/src/main/webapp/app/shared/grading-instruction-link-icon/grading-instruction-link-icon.component.ts +++ b/src/main/webapp/app/shared/grading-instruction-link-icon/grading-instruction-link-icon.component.ts @@ -1,22 +1,27 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { GradingInstruction } from 'app/exercises/shared/structured-grading-criterion/grading-instruction.model'; import { Feedback } from 'app/entities/feedback.model'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { faLink, faTrash } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { NgClass } from '@angular/common'; @Component({ selector: 'jhi-grading-instruction-link-icon', templateUrl: './grading-instruction-link-icon.component.html', + imports: [FaIconComponent, NgbTooltip, NgClass, ArtemisTranslatePipe], }) export class GradingInstructionLinkIconComponent implements OnInit { + private artemisTranslatePipe = inject(ArtemisTranslatePipe); + @Input() linkIcon = faLink; @Input() feedback: Feedback; + instruction: GradingInstruction | undefined; confirmIcon = faTrash; showConfirm = false; - constructor(private artemisTranslatePipe: ArtemisTranslatePipe) {} - ngOnInit(): void { this.instruction = this.feedback.gradingInstruction; } diff --git a/src/main/webapp/app/shared/grading-instruction-link-icon/grading-instruction-link-icon.module.ts b/src/main/webapp/app/shared/grading-instruction-link-icon/grading-instruction-link-icon.module.ts index 2225649438f7..46a830041367 100644 --- a/src/main/webapp/app/shared/grading-instruction-link-icon/grading-instruction-link-icon.module.ts +++ b/src/main/webapp/app/shared/grading-instruction-link-icon/grading-instruction-link-icon.module.ts @@ -4,8 +4,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { GradingInstructionLinkIconComponent } from 'app/shared/grading-instruction-link-icon/grading-instruction-link-icon.component'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [GradingInstructionLinkIconComponent], + imports: [ArtemisSharedModule, GradingInstructionLinkIconComponent], exports: [GradingInstructionLinkIconComponent], }) export class ArtemisGradingInstructionLinkIconModule {} diff --git a/src/main/webapp/app/shared/guard/pending-changes.guard.ts b/src/main/webapp/app/shared/guard/pending-changes.guard.ts index 294023cd6e42..34332f264bdf 100644 --- a/src/main/webapp/app/shared/guard/pending-changes.guard.ts +++ b/src/main/webapp/app/shared/guard/pending-changes.guard.ts @@ -1,11 +1,11 @@ import { CanDeactivate } from '@angular/router'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ComponentCanDeactivate } from 'app/shared/guard/can-deactivate.model'; @Injectable({ providedIn: 'root' }) export class PendingChangesGuard implements CanDeactivate { - constructor(private translateService: TranslateService) {} + private translateService = inject(TranslateService); /** * Function which returns whether a component can be deactivated diff --git a/src/main/webapp/app/shared/http/file.service.ts b/src/main/webapp/app/shared/http/file.service.ts index f739c44014ed..e4beba479b45 100644 --- a/src/main/webapp/app/shared/http/file.service.ts +++ b/src/main/webapp/app/shared/http/file.service.ts @@ -1,5 +1,5 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { lastValueFrom } from 'rxjs'; import { v4 as uuid } from 'uuid'; import { Observable } from 'rxjs'; @@ -8,10 +8,9 @@ import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/progr @Injectable({ providedIn: 'root' }) export class FileService { + private http = inject(HttpClient); private resourceUrl = 'api/files'; - constructor(private http: HttpClient) {} - /** * Fetches the template file for the given programming language * @param {ProgrammingLanguage} language diff --git a/src/main/webapp/app/shared/http/secure-link.directive.ts b/src/main/webapp/app/shared/http/secure-link.directive.ts index 8dbc4d75ac03..7253670e0bb3 100644 --- a/src/main/webapp/app/shared/http/secure-link.directive.ts +++ b/src/main/webapp/app/shared/http/secure-link.directive.ts @@ -1,13 +1,13 @@ -import { Directive, ElementRef } from '@angular/core'; +import { Directive, ElementRef, inject } from '@angular/core'; /** * Avoid Reverse Tabnabbing vulnerability: https://www.owasp.org/index.php/Reverse_Tabnabbing. */ -@Directive({ - selector: '[jhiSecureLink]', -}) +@Directive({ selector: '[jhiSecureLink]' }) export class SecureLinkDirective { - constructor(el: ElementRef) { + constructor() { + const el = inject(ElementRef); + el.nativeElement.target = '_blank'; el.nativeElement.rel = 'noopener noreferrer'; } diff --git a/src/main/webapp/app/shared/icon-card/icon-card.component.ts b/src/main/webapp/app/shared/icon-card/icon-card.component.ts index dc0c603000a2..a2222eaa8f7d 100644 --- a/src/main/webapp/app/shared/icon-card/icon-card.component.ts +++ b/src/main/webapp/app/shared/icon-card/icon-card.component.ts @@ -8,7 +8,6 @@ import { ArtemisSharedPipesModule } from 'app/shared/pipes/shared-pipes.module'; selector: 'jhi-icon-card', templateUrl: './icon-card.component.html', styleUrl: './icon-card.component.scss', - standalone: true, imports: [ArtemisSharedCommonModule, ArtemisSharedPipesModule], }) export class IconCardComponent { diff --git a/src/main/webapp/app/shared/image-cropper/component/image-cropper.component.ts b/src/main/webapp/app/shared/image-cropper/component/image-cropper.component.ts index b9d50c821e92..bc8fb40ba3a5 100644 --- a/src/main/webapp/app/shared/image-cropper/component/image-cropper.component.ts +++ b/src/main/webapp/app/shared/image-cropper/component/image-cropper.component.ts @@ -12,6 +12,7 @@ import { Output, SimpleChanges, ViewChild, + inject, } from '@angular/core'; import { DomSanitizer, SafeStyle, SafeUrl } from '@angular/platform-browser'; import { OutputFormat } from '../interfaces/cropper-options.interface'; @@ -31,13 +32,18 @@ import { ImageCroppedEvent } from 'app/shared/image-cropper/interfaces/image-cro // Note: Partially adapted to fit Artemis needs @Component({ - // eslint-disable-next-line @angular-eslint/component-selector selector: 'image-cropper', templateUrl: './image-cropper.component.html', styleUrls: ['./image-cropper.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) export class ImageCropperComponent implements OnChanges, OnInit { + private cropService = inject(CropService); + private cropperPositionService = inject(CropperPositionService); + private loadImageService = inject(LoadImageService); + private sanitizer = inject(DomSanitizer); + private changeDetector = inject(ChangeDetectorRef); + settings = new CropperSettings(); setImageMaxSizeRetries = 0; moveStart: MoveStart; @@ -98,13 +104,7 @@ export class ImageCropperComponent implements OnChanges, OnInit { @Output() cropperReady = new EventEmitter(); @Output() loadImageFailed = new EventEmitter(); - constructor( - private cropService: CropService, - private cropperPositionService: CropperPositionService, - private loadImageService: LoadImageService, - private sanitizer: DomSanitizer, - private changeDetector: ChangeDetectorRef, - ) { + constructor() { this.reset(); } diff --git a/src/main/webapp/app/shared/image-cropper/image-cropper.module.ts b/src/main/webapp/app/shared/image-cropper/image-cropper.module.ts deleted file mode 100644 index 157ca1d3c2dc..000000000000 --- a/src/main/webapp/app/shared/image-cropper/image-cropper.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { ImageCropperComponent } from './component/image-cropper.component'; - -@NgModule({ - imports: [CommonModule], - declarations: [ImageCropperComponent], - exports: [ImageCropperComponent], -}) -export class ImageCropperModule {} diff --git a/src/main/webapp/app/shared/image/cacheable-image.service.ts b/src/main/webapp/app/shared/image/cacheable-image.service.ts index d6e0341a115f..159edc2e8965 100644 --- a/src/main/webapp/app/shared/image/cacheable-image.service.ts +++ b/src/main/webapp/app/shared/image/cacheable-image.service.ts @@ -1,4 +1,4 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { Cacheable, LocalStorageStrategy } from 'ts-cacheable'; import { HttpClient } from '@angular/common/http'; import { Observable, Subject, Subscription, UnaryFunction, pipe } from 'rxjs'; @@ -29,12 +29,12 @@ export interface ICacheableImageService { @Injectable({ providedIn: 'root' }) export class CacheableImageService implements ICacheableImageService, OnDestroy { + private accountService = inject(AccountService); + private httpClient = inject(HttpClient); + private userChangeSubscription: Subscription; - constructor( - private accountService: AccountService, - private httpClient: HttpClient, - ) { + constructor() { this.init(); } diff --git a/src/main/webapp/app/shared/image/secured-image.component.ts b/src/main/webapp/app/shared/image/secured-image.component.ts index c8390288cba2..deb63f0244a1 100644 --- a/src/main/webapp/app/shared/image/secured-image.component.ts +++ b/src/main/webapp/app/shared/image/secured-image.component.ts @@ -1,9 +1,10 @@ -import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, inject } from '@angular/core'; import { BehaviorSubject, Observable, isObservable, of } from 'rxjs'; import { catchError, filter, map, switchMap, tap } from 'rxjs/operators'; import { DomSanitizer } from '@angular/platform-browser'; import { CacheableImageService } from 'app/shared/image/cacheable-image.service'; import { base64StringToBlob } from 'app/utils/blob-util'; +import { AsyncPipe } from '@angular/common'; // Status that is emitted to the client to describe the loading status of the picture export const enum ImageLoadingStatus { @@ -36,8 +37,13 @@ export enum CachingStrategy { alt } `, + imports: [AsyncPipe], }) export class SecuredImageComponent implements OnChanges, OnInit { + private domSanitizer = inject(DomSanitizer); + private cacheableImageService = inject(CacheableImageService); + element = inject(ElementRef); + // This part just creates an rxjs stream from the src // this makes sure that we can handle it when the src changes // or even when the component gets destroyed @@ -62,18 +68,13 @@ export class SecuredImageComponent implements OnChanges, OnInit { ); } - ngOnChanges(): void { + ngOnChanges() { if (this.srcSubject) { this.srcSubject.next(this.src); } } // we need HttpClient to load the image and DomSanitizer to trust the url - constructor( - private domSanitizer: DomSanitizer, - private cacheableImageService: CacheableImageService, - public element: ElementRef, - ) {} // triggers the reload of the picture when the user clicks on a button retryLoadImage() { diff --git a/src/main/webapp/app/shared/import-list/import-table.component.ts b/src/main/webapp/app/shared/import-list/import-table.component.ts index 2a0a3ee92269..70aa1aac4fcb 100644 --- a/src/main/webapp/app/shared/import-list/import-table.component.ts +++ b/src/main/webapp/app/shared/import-list/import-table.component.ts @@ -8,6 +8,7 @@ import { onError } from 'app/shared/util/global.utils'; import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; import { faSort, faSortDown, faSortUp, faSpinner } from '@fortawesome/free-solid-svg-icons'; import { BaseApiHttpService } from 'app/course/learning-paths/services/base-api-http.service'; +import { NgbPagination } from '@ng-bootstrap/ng-bootstrap'; /** * An abstract component intended for cases where a resource needs to be imported from one course into another. @@ -21,8 +22,7 @@ export type Column = { @Component({ selector: 'jhi-import-table', - standalone: true, - imports: [ArtemisSharedCommonModule], + imports: [ArtemisSharedCommonModule, NgbPagination], templateUrl: './import-table.component.html', styleUrl: './import-table.component.scss', }) @@ -37,9 +37,9 @@ export class ImportTableComponent { private readonly alertService = inject(AlertService); private readonly pagingService = inject(PagingService); - readonly columns = input.required[]>(); + columns = input.required[]>(); readonly columnBaseTranslationKey = input.required(); - readonly disabledIds = input([]); + disabledIds = input([]); readonly numberOfColumns = computed(() => this.columns().length + 2); readonly onRowSelection = output(); diff --git a/src/main/webapp/app/shared/import/import.component.ts b/src/main/webapp/app/shared/import/import.component.ts index d8440b6a57d9..abf4a5f3b94d 100644 --- a/src/main/webapp/app/shared/import/import.component.ts +++ b/src/main/webapp/app/shared/import/import.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { Router } from '@angular/router'; import { faCheck, faSort } from '@fortawesome/free-solid-svg-icons'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; @@ -19,8 +19,14 @@ export type Column = { getProperty(entity: T): string | undefined; }; -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class ImportComponent implements OnInit { + protected router = inject(Router); + private sortService = inject(SortService); + protected activeModal = inject(NgbActiveModal); + loading = false; content: SearchResult; total = 0; @@ -44,12 +50,7 @@ export abstract class ImportComponent implements OnInit { protected readonly search = new Subject(); protected readonly sort = new Subject(); - protected constructor( - protected router: Router, - private sortService: SortService, - protected activeModal: NgbActiveModal, - protected pagingService?: PagingService, - ) {} + protected constructor(protected pagingService?: PagingService) {} get page(): number { return this.state.page; @@ -106,11 +107,11 @@ export abstract class ImportComponent implements OnInit { * Gives the ID for any item in the table, so that it can be tracked/identified by ngFor * * @template T - * @param index The index of the element in the ngFor + * @param _index The index of the element in the ngFor * @param {T} item The item itself * @returns The ID of the item */ - trackId(index: number, item: T): number { + trackId(_index: number, item: T): number { return item.id!; } diff --git a/src/main/webapp/app/shared/info-panel/info-panel.component.ts b/src/main/webapp/app/shared/info-panel/info-panel.component.ts index 617fc3e63c0a..c12faca7744d 100644 --- a/src/main/webapp/app/shared/info-panel/info-panel.component.ts +++ b/src/main/webapp/app/shared/info-panel/info-panel.component.ts @@ -8,6 +8,4 @@ import { Component, Input } from '@angular/core'; export class InfoPanelComponent { @Input() panelHeader: string; @Input() panelDescriptionHeader: string; - - constructor() {} } diff --git a/src/main/webapp/app/shared/info-panel/info-panel.module.ts b/src/main/webapp/app/shared/info-panel/info-panel.module.ts index ab86f1339853..0411c3dbf043 100644 --- a/src/main/webapp/app/shared/info-panel/info-panel.module.ts +++ b/src/main/webapp/app/shared/info-panel/info-panel.module.ts @@ -4,8 +4,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { InfoPanelComponent } from './info-panel.component'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [InfoPanelComponent], + imports: [ArtemisSharedModule, InfoPanelComponent], exports: [InfoPanelComponent], }) export class ArtemisInfoPanelModule {} diff --git a/src/main/webapp/app/shared/information-box/information-box.component.ts b/src/main/webapp/app/shared/information-box/information-box.component.ts index 5be6212f7277..c4ed7d387507 100644 --- a/src/main/webapp/app/shared/information-box/information-box.component.ts +++ b/src/main/webapp/app/shared/information-box/information-box.component.ts @@ -42,7 +42,6 @@ export interface StringNumberContent { export type InformationBoxContent = StudentExamContent | DateContent | ExerciseContent | DifficultyLevelContent | StringNumberContent; @Component({ - standalone: true, imports: [ArtemisSharedModule, ArtemisSharedComponentModule], selector: 'jhi-information-box', templateUrl: './information-box.component.html', diff --git a/src/main/webapp/app/shared/language/translate.directive.ts b/src/main/webapp/app/shared/language/translate.directive.ts index cb40182df64f..35beeb8dba62 100644 --- a/src/main/webapp/app/shared/language/translate.directive.ts +++ b/src/main/webapp/app/shared/language/translate.directive.ts @@ -1,4 +1,4 @@ -import { Directive, ElementRef, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { Directive, ElementRef, Input, OnChanges, OnDestroy, OnInit, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { translationNotFoundMessage } from 'app/core/config/translation.config'; import { Subject } from 'rxjs'; @@ -9,19 +9,16 @@ import { takeUntil } from 'rxjs/operators'; */ @Directive({ selector: '[jhiTranslate]', - standalone: true, }) export class TranslateDirective implements OnChanges, OnInit, OnDestroy { + private el = inject(ElementRef); + private translateService = inject(TranslateService); + @Input() jhiTranslate!: string; @Input() translateValues?: { [key: string]: unknown }; private readonly directiveDestroyed = new Subject(); - constructor( - private el: ElementRef, - private translateService: TranslateService, - ) {} - ngOnInit(): void { this.translateService.onLangChange.pipe(takeUntil(this.directiveDestroyed)).subscribe(() => { this.getTranslation(); @@ -31,7 +28,7 @@ export class TranslateDirective implements OnChanges, OnInit, OnDestroy { }); } - ngOnChanges(): void { + ngOnChanges() { this.getTranslation(); } diff --git a/src/main/webapp/app/shared/layouts/error/error.component.ts b/src/main/webapp/app/shared/layouts/error/error.component.ts index d6a2dd358cc3..f408c980c7fa 100644 --- a/src/main/webapp/app/shared/layouts/error/error.component.ts +++ b/src/main/webapp/app/shared/layouts/error/error.component.ts @@ -1,17 +1,19 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; +import { TranslateDirective } from '../../language/translate.directive'; @Component({ selector: 'jhi-error', templateUrl: './error.component.html', + imports: [TranslateDirective], }) export class ErrorComponent implements OnInit { + private route = inject(ActivatedRoute); + errorMessage: string; error403: boolean; error404: boolean; - constructor(private route: ActivatedRoute) {} - ngOnInit() { this.route.data.subscribe((routeData) => { if (routeData.error403) { diff --git a/src/main/webapp/app/shared/layouts/error/error.route.ts b/src/main/webapp/app/shared/layouts/error/error.route.ts index ac642f572bff..67e1e2ad0bea 100644 --- a/src/main/webapp/app/shared/layouts/error/error.route.ts +++ b/src/main/webapp/app/shared/layouts/error/error.route.ts @@ -1,12 +1,9 @@ import { Routes } from '@angular/router'; -import { ErrorComponent } from './error.component'; -import { OrionOutdatedComponent } from 'app/shared/orion/outdated-plugin-warning/orion-outdated.component'; - export const errorRoute: Routes = [ { path: 'error', - component: ErrorComponent, + loadComponent: () => import('./error.component').then((m) => m.ErrorComponent), data: { authorities: [], pageTitle: 'error.title', @@ -14,7 +11,7 @@ export const errorRoute: Routes = [ }, { path: 'accessdenied', - component: ErrorComponent, + loadComponent: () => import('./error.component').then((m) => m.ErrorComponent), data: { authorities: [], pageTitle: 'error.title', @@ -23,7 +20,7 @@ export const errorRoute: Routes = [ }, { path: 'orion-outdated', - component: OrionOutdatedComponent, + loadComponent: () => import('app/shared/orion/outdated-plugin-warning/orion-outdated.component').then((m) => m.OrionOutdatedComponent), data: { authorities: [], pageTitle: 'Outdated Orion Version', diff --git a/src/main/webapp/app/shared/layouts/footer/footer.component.ts b/src/main/webapp/app/shared/layouts/footer/footer.component.ts index 84aec9857263..64cc8e3420b0 100644 --- a/src/main/webapp/app/shared/layouts/footer/footer.component.ts +++ b/src/main/webapp/app/shared/layouts/footer/footer.component.ts @@ -1,12 +1,18 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; +import { TranslateDirective } from '../../language/translate.directive'; +import { RouterLink } from '@angular/router'; +import { ArtemisTranslatePipe } from '../../pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-footer', templateUrl: './footer.component.html', styleUrls: ['./footer.scss'], + imports: [TranslateDirective, RouterLink, ArtemisTranslatePipe], }) export class FooterComponent implements OnInit { + private profileService = inject(ProfileService); + readonly RELEASE_URL = 'https://github.com/ls1intum/Artemis/releases'; readonly FEEDBACK_URL = 'https://github.com/ls1intum/Artemis/issues/new/choose'; @@ -18,8 +24,6 @@ export class FooterComponent implements OnInit { isTestServer: boolean; isProduction: boolean; - constructor(private profileService: ProfileService) {} - ngOnInit(): void { this.profileService.getProfileInfo().subscribe((profileInfo) => { this.contact = profileInfo.contact; diff --git a/src/main/webapp/app/shared/layouts/navbar/active-menu.directive.ts b/src/main/webapp/app/shared/layouts/navbar/active-menu.directive.ts index de4296e68ebb..41d8a8ae10f2 100644 --- a/src/main/webapp/app/shared/layouts/navbar/active-menu.directive.ts +++ b/src/main/webapp/app/shared/layouts/navbar/active-menu.directive.ts @@ -1,17 +1,13 @@ -import { Directive, ElementRef, Input, OnInit, Renderer2 } from '@angular/core'; +import { Directive, ElementRef, Input, OnInit, Renderer2, inject } from '@angular/core'; import { LangChangeEvent, TranslateService } from '@ngx-translate/core'; -@Directive({ - selector: '[jhiActiveMenu]', -}) +@Directive({ selector: '[jhiActiveMenu]' }) export class ActiveMenuDirective implements OnInit { - @Input() jhiActiveMenu: string; + private element = inject(ElementRef); + private renderer = inject(Renderer2); + private translateService = inject(TranslateService); - constructor( - private element: ElementRef, - private renderer: Renderer2, - private translateService: TranslateService, - ) {} + @Input() jhiActiveMenu: string; ngOnInit() { this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { diff --git a/src/main/webapp/app/shared/layouts/navbar/entity-title.service.ts b/src/main/webapp/app/shared/layouts/navbar/entity-title.service.ts index 3ab54964001f..938562e912a4 100644 --- a/src/main/webapp/app/shared/layouts/navbar/entity-title.service.ts +++ b/src/main/webapp/app/shared/layouts/navbar/entity-title.service.ts @@ -1,5 +1,5 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { captureException } from '@sentry/angular'; import { Exercise } from 'app/entities/exercise.model'; import { EMPTY, Observable, ReplaySubject, Subject } from 'rxjs'; @@ -22,9 +22,9 @@ const FETCH_FALLBACK_TIMEOUT = 3000; */ @Injectable({ providedIn: 'root' }) export class EntityTitleService { - private readonly titleSubjects = new Map; timeout?: ReturnType }>(); + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + private readonly titleSubjects = new Map; timeout?: ReturnType }>(); /** * Returns an observable that will provide the title of the entity. diff --git a/src/main/webapp/app/shared/layouts/navbar/navbar.component.html b/src/main/webapp/app/shared/layouts/navbar/navbar.component.html index 209186817f17..e51a5a10990c 100644 --- a/src/main/webapp/app/shared/layouts/navbar/navbar.component.html +++ b/src/main/webapp/app/shared/layouts/navbar/navbar.component.html @@ -337,7 +337,7 @@
    - +
    Artemis
    @@ -185,9 +155,7 @@
    - + Dear fullName @@ -201,11 +169,8 @@
    - + - @@ -215,12 +180,9 @@ - +
    -

    Regards,
    @@ -231,9 +193,7 @@ - +

    @@ -244,8 +204,8 @@ + ADDITIONAL AUXILIARY FRAGMENTS + --> @@ -291,17 +251,17 @@ - - - - - - - - + + + + + + + + + - @@ -319,10 +279,9 @@ - - + + - diff --git a/src/main/resources/templates/mail/notification/plagiarismCaseEmail.html b/src/main/resources/templates/mail/notification/plagiarismCaseEmail.html index af46cea46eb0..2359c6273d16 100644 --- a/src/main/resources/templates/mail/notification/plagiarismCaseEmail.html +++ b/src/main/resources/templates/mail/notification/plagiarismCaseEmail.html @@ -1,15 +1,15 @@ - + - +

    Post Content

    - +
    diff --git a/src/main/resources/templates/mail/notification/plagiarismVerdictEmail.html b/src/main/resources/templates/mail/notification/plagiarismVerdictEmail.html index bf4fe76603d9..b1b5121b34cb 100644 --- a/src/main/resources/templates/mail/notification/plagiarismVerdictEmail.html +++ b/src/main/resources/templates/mail/notification/plagiarismVerdictEmail.html @@ -1,24 +1,24 @@ - + - +
    - +
    Notification Content for Plagiarism Verdict
    - +
    - +
    - +
    - + diff --git a/src/main/resources/templates/mail/notification/tutorialGroupBasicEmail.html b/src/main/resources/templates/mail/notification/tutorialGroupBasicEmail.html index 203ba8503c0c..634048231e72 100644 --- a/src/main/resources/templates/mail/notification/tutorialGroupBasicEmail.html +++ b/src/main/resources/templates/mail/notification/tutorialGroupBasicEmail.html @@ -1,11 +1,11 @@ - + - +
    - +
    - +
    - +
    - + diff --git a/src/main/resources/templates/mail/notification/tutorialGroupDeletedEmail.html b/src/main/resources/templates/mail/notification/tutorialGroupDeletedEmail.html index cf85cd6eeae8..d3e7878ad259 100644 --- a/src/main/resources/templates/mail/notification/tutorialGroupDeletedEmail.html +++ b/src/main/resources/templates/mail/notification/tutorialGroupDeletedEmail.html @@ -1,20 +1,20 @@ - + - +
    - +
    - +
    - + diff --git a/src/main/resources/templates/mail/notification/tutorialGroupUpdatedEmail.html b/src/main/resources/templates/mail/notification/tutorialGroupUpdatedEmail.html index 7abf30001001..81aeddfea72f 100644 --- a/src/main/resources/templates/mail/notification/tutorialGroupUpdatedEmail.html +++ b/src/main/resources/templates/mail/notification/tutorialGroupUpdatedEmail.html @@ -1,22 +1,22 @@ - + - +
    - +
    - - + +
    - +
    - + diff --git a/src/main/resources/templates/mail/successfulDataExportsAdminEmail.html b/src/main/resources/templates/mail/successfulDataExportsAdminEmail.html index 7903a1e51573..dcf74bb28272 100644 --- a/src/main/resources/templates/mail/successfulDataExportsAdminEmail.html +++ b/src/main/resources/templates/mail/successfulDataExportsAdminEmail.html @@ -2,11 +2,11 @@ Data exports created - - + + - +

    Successful data exports

    diff --git a/src/main/resources/templates/mail/weeklySummary.html b/src/main/resources/templates/mail/weeklySummary.html index a531ce2d5c58..0e9e83d31fee 100644 --- a/src/main/resources/templates/mail/weeklySummary.html +++ b/src/main/resources/templates/mail/weeklySummary.html @@ -2,14 +2,14 @@ Weekly Summary - - + + - +
    - +
    This is your Artemis summary of the last seven days.
    @@ -56,9 +56,9 @@
    - +
    - + diff --git a/src/main/webapp/app/core/about-us/about-us.component.ts b/src/main/webapp/app/core/about-us/about-us.component.ts index 42ba47b5093a..4fff2553b602 100644 --- a/src/main/webapp/app/core/about-us/about-us.component.ts +++ b/src/main/webapp/app/core/about-us/about-us.component.ts @@ -44,7 +44,7 @@ export class AboutUsComponent implements OnInit { ['learningAnalytics', { learningAnalyticsUrl: 'https://docs.artemis.cit.tum.de/user/learning-analytics/' }], ['adaptiveLearning', { adaptiveLearningUrl: 'https://docs.artemis.cit.tum.de/user/adaptive-learning/' }], ['tutorialGroups', { tutorialGroupsUrl: 'https://docs.artemis.cit.tum.de/user/tutorialgroups/' }], - ['iris', { irisUrl: 'https://artemis.cit.tum.de/about-iris' }], + ['iris', { irisUrl: 'https://artemis.tum.de/about-iris' }], ['scalable', { scalingUrl: 'https://docs.artemis.cit.tum.de/user/scaling/' }], ['highUserSatisfaction', { userExperienceUrl: 'https://docs.artemis.cit.tum.de/user/user-experience/' }], ['customizable', { customizableUrl: 'https://docs.artemis.cit.tum.de/user/courses/customizable' }], diff --git a/src/test/java/de/tum/cit/aet/artemis/programming/icl/RepositoryUriTest.java b/src/test/java/de/tum/cit/aet/artemis/programming/icl/RepositoryUriTest.java index bf124d494ccc..d1c816be5c66 100644 --- a/src/test/java/de/tum/cit/aet/artemis/programming/icl/RepositoryUriTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/programming/icl/RepositoryUriTest.java @@ -23,7 +23,7 @@ class RepositoryUriTest { @Test void testValidUrlWithGit() { - String urlString = "https://artemis.cit.tum.de/git/key/key-repositoryslug.git"; + String urlString = "https://artemis.tum.de/git/key/key-repositoryslug.git"; assertThatCode(() -> { LocalVCRepositoryUri uri = new LocalVCRepositoryUri(urlString); @@ -40,21 +40,21 @@ void testValidUrlWithGit() { @Test void testUrlWithoutGit() { - String urlString = "https://artemis.cit.tum.de/key/key-repositoryslug.git"; + String urlString = "https://artemis.tum.de/key/key-repositoryslug.git"; assertThatThrownBy(() -> new LocalVCRepositoryUri(urlString)).isInstanceOf(LocalVCInternalException.class) .hasMessageContaining("Invalid local VC Repository URI: 'git' directory not found in the URL"); } @Test void testUrlWithInsufficientSegments() { - String urlString = "https://artemis.cit.tum.de/git/key"; + String urlString = "https://artemis.tum.de/git/key"; assertThatThrownBy(() -> new LocalVCRepositoryUri(urlString)).isInstanceOf(LocalVCInternalException.class) .hasMessageContaining("URL does not contain enough segments after 'git'"); } @Test void testUrlRepositorySlugWithoutGitSuffix() { - String urlString = "https://artemis.cit.tum.de/git/key/key-repositoryslug"; + String urlString = "https://artemis.tum.de/git/key/key-repositoryslug"; assertThatThrownBy(() -> new LocalVCRepositoryUri(urlString)).isInstanceOf(LocalVCInternalException.class) .hasMessageContaining("Repository slug segment 'key-repositoryslug' does not end with '.git'"); } @@ -62,10 +62,10 @@ void testUrlRepositorySlugWithoutGitSuffix() { @Test void testLocalRepositoryPath() throws Exception { Path repositoryPath = Paths.get("/local/path/projectX/projectX-repo/.git"); - URL localVCServerUrl = new URI("https://artemis.cit.tum.de").toURL(); + URL localVCServerUrl = new URI("https://artemis.tum.de").toURL(); LocalVCRepositoryUri uri = new LocalVCRepositoryUri(repositoryPath, localVCServerUrl); - assertThat(uri.getURI().toString()).isEqualTo("https://artemis.cit.tum.de/git/projectX/projectX-repo.git"); + assertThat(uri.getURI().toString()).isEqualTo("https://artemis.tum.de/git/projectX/projectX-repo.git"); assertThat(uri.getProjectKey()).isEqualTo("projectX"); assertThat(uri.repositorySlug()).isEqualTo("projectX-repo"); assertThat(uri.getRepositoryTypeOrUserName()).isEqualTo("repo"); @@ -75,10 +75,10 @@ void testLocalRepositoryPath() throws Exception { @Test void testRemoteRepositoryPath() throws Exception { Path repositoryPath = Paths.get("/remote/path/projectY/projectY-repo"); - URL localVCServerUrl = new URI("https://artemis.cit.tum.de").toURL(); + URL localVCServerUrl = new URI("https://artemis.tum.de").toURL(); LocalVCRepositoryUri uri = new LocalVCRepositoryUri(repositoryPath, localVCServerUrl); - assertThat(uri.getURI().toString()).isEqualTo("https://artemis.cit.tum.de/git/projectY/projectY-repo.git"); + assertThat(uri.getURI().toString()).isEqualTo("https://artemis.tum.de/git/projectY/projectY-repo.git"); assertThat(uri.getProjectKey()).isEqualTo("projectY"); assertThat(uri.repositorySlug()).isEqualTo("projectY-repo"); assertThat(uri.getRepositoryTypeOrUserName()).isEqualTo("repo"); @@ -90,7 +90,7 @@ void testInvalidRepositoryPath() { Path repositoryPath = Paths.get("/invalid/path"); URL localVCServerUrl; try { - localVCServerUrl = new URI("https://artemis.cit.tum.de").toURL(); + localVCServerUrl = new URI("https://artemis.tum.de").toURL(); assertThatThrownBy(() -> new LocalVCRepositoryUri(repositoryPath, localVCServerUrl)).isInstanceOf(LocalVCInternalException.class) .hasMessageContaining("Invalid project key and repository slug: invalid, path"); } @@ -103,28 +103,28 @@ void testInvalidRepositoryPath() { void testConstructorWithValidData() throws Exception { String projectKey = "projectX"; String repositorySlug = "my-repo"; - URL localVCBaseUrl = new URI("https://artemis.cit.tum.de").toURL(); + URL localVCBaseUrl = new URI("https://artemis.tum.de").toURL(); LocalVCRepositoryUri uri = new LocalVCRepositoryUri(projectKey, repositorySlug, localVCBaseUrl); assertThat(uri.getProjectKey()).isEqualTo(projectKey); assertThat(uri.getRepositoryTypeOrUserName()).isEqualTo(repositorySlug); assertThat(uri.isPracticeRepository()).isFalse(); - assertThat(uri.getURI().toString()).isEqualTo("https://artemis.cit.tum.de/git/projectX/my-repo.git"); + assertThat(uri.getURI().toString()).isEqualTo("https://artemis.tum.de/git/projectX/my-repo.git"); } @Test void testConstructorWithPracticeRepository() throws Exception { String projectKey = "projectX"; String repositorySlug = "projectX-practice-my-repo"; - URL localVCBaseUrl = new URI("https://artemis.cit.tum.de").toURL(); + URL localVCBaseUrl = new URI("https://artemis.tum.de").toURL(); LocalVCRepositoryUri uri = new LocalVCRepositoryUri(projectKey, repositorySlug, localVCBaseUrl); assertThat(uri.getProjectKey()).isEqualTo(projectKey); assertThat(uri.getRepositoryTypeOrUserName()).isEqualTo("my-repo"); assertThat(uri.isPracticeRepository()).isTrue(); - assertThat(uri.getURI().toString()).isEqualTo("https://artemis.cit.tum.de/git/projectX/projectX-practice-my-repo.git"); + assertThat(uri.getURI().toString()).isEqualTo("https://artemis.tum.de/git/projectX/projectX-practice-my-repo.git"); } @Test diff --git a/src/test/javascript/spec/service/external-cloning.service.spec.ts b/src/test/javascript/spec/service/external-cloning.service.spec.ts index ff1982a2bbe3..57afb1b1db03 100644 --- a/src/test/javascript/spec/service/external-cloning.service.spec.ts +++ b/src/test/javascript/spec/service/external-cloning.service.spec.ts @@ -3,7 +3,7 @@ import { ExternalCloningService } from 'app/exercises/programming/shared/service describe('ExternalCloningService', () => { let service: ExternalCloningService; - const baseUrl = 'https://artemis.cit.tum.de'; + const baseUrl = 'https://artemis.tum.de'; beforeEach(() => { TestBed.configureTestingModule({}); @@ -12,7 +12,7 @@ describe('ExternalCloningService', () => { it('should build source tree url correctly', () => { const cloneUrl = baseUrl + '/git/reo.git'; - const expectedUrl = `sourcetree://cloneRepo?type=stash&cloneUrl=https://artemis.cit.tum.de/git/reo.git&baseWebUrl=https://artemis.cit.tum.de`; + const expectedUrl = `sourcetree://cloneRepo?type=stash&cloneUrl=https://artemis.tum.de/git/reo.git&baseWebUrl=https://artemis.tum.de`; expect(service.buildSourceTreeUrl(baseUrl, cloneUrl)).toEqual(expectedUrl); }); @@ -23,7 +23,7 @@ describe('ExternalCloningService', () => { it('should build ide deeplink url correctly', () => { const cloneUrl = baseUrl + '/git/repo.git'; const ide = { name: 'VS Code', deepLink: 'vscode://vscode.git/clone?url={cloneUrl}' }; - const expectedUrl = 'vscode://vscode.git/clone?url=https%3A%2F%2Fartemis.cit.tum.de%2Fgit%2Frepo.git'; + const expectedUrl = 'vscode://vscode.git/clone?url=https%3A%2F%2Fartemis.tum.de%2Fgit%2Frepo.git'; expect(service.buildIdeUrl(cloneUrl, ide)).toEqual(expectedUrl); }); From 9c85d579c60b2926263ae14f14776a0b323350e7 Mon Sep 17 00:00:00 2001 From: Julian Gassner Date: Sun, 12 Jan 2025 11:41:47 +0100 Subject: [PATCH 07/16] Communication: Improve visibility of pinned messages (#10117) --- .../conversation-messages.component.ts | 14 ++++++++++++-- .../discussion-section.component.ts | 15 +++++++++++++-- .../posting-header/posting-header.component.html | 3 +++ .../posting-header/posting-header.component.ts | 7 ++++++- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts index ddac0b0efacb..81e5b88d053c 100644 --- a/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts +++ b/src/main/webapp/app/overview/course-conversations/layout/conversation-messages/conversation-messages.component.ts @@ -22,7 +22,7 @@ import { Conversation, ConversationDTO } from 'app/entities/metis/conversation/c import { Subject, map, takeUntil } from 'rxjs'; import { Post } from 'app/entities/metis/post.model'; import { Course } from 'app/entities/course.model'; -import { PageType, PostContextFilter, PostSortCriterion, SortDirection } from 'app/shared/metis/metis.util'; +import { DisplayPriority, PageType, PostContextFilter, PostSortCriterion, SortDirection } from 'app/shared/metis/metis.util'; import { MetisService } from 'app/shared/metis/metis.service'; import { Channel, getAsChannelDTO, isChannelDTO } from 'app/entities/metis/conversation/channel.model'; import { GroupChat, isGroupChatDTO } from 'app/entities/metis/conversation/group-chat.model'; @@ -248,7 +248,11 @@ export class ConversationMessagesComponent implements OnInit, AfterViewInit, OnD return; } - const sortedPosts = this.posts.sort((a, b) => { + // Separate pinned posts into their own group + const pinnedPosts = this.posts.filter((post) => post.displayPriority === DisplayPriority.PINNED); + const unpinnedPosts = this.posts.filter((post) => post.displayPriority !== DisplayPriority.PINNED); + + const sortedPosts = unpinnedPosts.sort((a, b) => { const aDate = (a as any).creationDateDayjs; const bDate = (b as any).creationDateDayjs; return aDate?.valueOf() - bDate?.valueOf(); @@ -284,6 +288,12 @@ export class ConversationMessagesComponent implements OnInit, AfterViewInit, OnD } groups.push(currentGroup); + + // Only add pinned group if pinned posts exist + if (pinnedPosts.length > 0) { + groups.unshift({ author: undefined, posts: pinnedPosts }); + } + this.groupedPosts = groups; this.cdr.detectChanges(); } diff --git a/src/main/webapp/app/overview/discussion-section/discussion-section.component.ts b/src/main/webapp/app/overview/discussion-section/discussion-section.component.ts index 63d5873cc3b1..d272247a86cf 100644 --- a/src/main/webapp/app/overview/discussion-section/discussion-section.component.ts +++ b/src/main/webapp/app/overview/discussion-section/discussion-section.component.ts @@ -2,7 +2,7 @@ import { AfterViewInit, Component, ElementRef, OnDestroy, QueryList, ViewChild, import interact from 'interactjs'; import { Exercise } from 'app/entities/exercise.model'; import { Lecture } from 'app/entities/lecture.model'; -import { PageType, PostSortCriterion, SortDirection } from 'app/shared/metis/metis.util'; +import { DisplayPriority, PageType, PostSortCriterion, SortDirection } from 'app/shared/metis/metis.util'; import { ActivatedRoute, Params, Router } from '@angular/router'; import { Subject, combineLatest, map, takeUntil } from 'rxjs'; import { MetisService } from 'app/shared/metis/metis.service'; @@ -91,7 +91,18 @@ export class DiscussionSectionComponent extends CourseDiscussionDirective implem if (this.content) { this.previousScrollDistanceFromTop = this.content.nativeElement.scrollHeight - this.content.nativeElement.scrollTop; } - this.posts = posts.slice().reverse(); + this.posts = posts + .slice() + .sort((a, b) => { + if (a.displayPriority === DisplayPriority.PINNED && b.displayPriority !== DisplayPriority.PINNED) { + return 1; + } + if (a.displayPriority !== DisplayPriority.PINNED && b.displayPriority === DisplayPriority.PINNED) { + return -1; + } + return 0; + }) + .reverse(); this.isLoading = false; if (this.currentPostId && this.posts.length > 0) { this.currentPost = this.posts.find((post) => post.id === this.currentPostId); diff --git a/src/main/webapp/app/shared/metis/posting-header/posting-header.component.html b/src/main/webapp/app/shared/metis/posting-header/posting-header.component.html index 96117b859519..b43356727e13 100644 --- a/src/main/webapp/app/shared/metis/posting-header/posting-header.component.html +++ b/src/main/webapp/app/shared/metis/posting-header/posting-header.component.html @@ -42,6 +42,9 @@ [ngbTooltip]="'artemisApp.metis.post.postMarkedAsResolvedTooltip' | artemisTranslate" /> } + @if (isPostPinned()) { + + } @if ((!!isCommunicationPage() && !lastReadDate()) || (lastReadDate() && creationDate && isAfter && !isAuthorOfPosting)) { } diff --git a/src/main/webapp/app/shared/metis/posting-header/posting-header.component.ts b/src/main/webapp/app/shared/metis/posting-header/posting-header.component.ts index fe3aaee9cef7..41efaf548f94 100644 --- a/src/main/webapp/app/shared/metis/posting-header/posting-header.component.ts +++ b/src/main/webapp/app/shared/metis/posting-header/posting-header.component.ts @@ -10,7 +10,7 @@ import { MetisService } from 'app/shared/metis/metis.service'; import { AccountService } from 'app/core/auth/account.service'; import { tap } from 'rxjs'; import { faUser, faUserCheck, faUserGraduate } from '@fortawesome/free-solid-svg-icons'; -import { UserRole } from 'app/shared/metis/metis.util'; +import { DisplayPriority, UserRole } from 'app/shared/metis/metis.util'; import { AnswerPost } from 'app/entities/metis/answer-post.model'; import { Post } from 'app/entities/metis/post.model'; @@ -56,6 +56,11 @@ export class PostingHeaderComponent implements OnInit, OnDestroy, OnChanges { return this.isPost(p) && p.resolved === true; }); + isPostPinned = computed(() => { + const p = this.posting(); + return this.isPost(p) && p.displayPriority == DisplayPriority.PINNED; + }); + /** * on initialization: determines if user is author of posting by invoking the metis service, * determines if posting is of today and sets the today flag to be shown in the header of the posting From f974c5e52d68484f1927037ce3a38cd8c7962801 Mon Sep 17 00:00:00 2001 From: Anish Goyal <123313052+Anishyou@users.noreply.github.com> Date: Sun, 12 Jan 2025 11:44:37 +0100 Subject: [PATCH 08/16] Programming exercises: Update points automatically when the result changes (#10113) --- .../exercise-headers-information.component.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers-information/exercise-headers-information.component.ts b/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers-information/exercise-headers-information.component.ts index 8ec76214ddee..57c245a224fa 100644 --- a/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers-information/exercise-headers-information.component.ts +++ b/src/main/webapp/app/exercises/shared/exercise-headers/exercise-headers-information/exercise-headers-information.component.ts @@ -79,6 +79,7 @@ export class ExerciseHeadersInformationComponent implements OnInit, OnChanges { const latestRatedResult = this.studentParticipation.results.filter((result) => result.rated).first(); if (latestRatedResult) { this.achievedPoints = roundValueSpecifiedByCourseSettings((latestRatedResult.score! * this.exercise.maxPoints!) / 100, this.course) ?? 0; + this.updatePointsItem(); } } } @@ -93,6 +94,13 @@ export class ExerciseHeadersInformationComponent implements OnInit, OnChanges { this.addCategoryItems(); } + updatePointsItem() { + const pointsItemIndex = this.informationBoxItems.findIndex((item) => item.title === 'artemisApp.courseOverview.exerciseDetails.points'); + if (pointsItemIndex !== -1) { + this.informationBoxItems[pointsItemIndex] = this.getPointsItem('points', this.exercise.maxPoints!, this.achievedPoints); + } + } + addPointsItems() { const { maxPoints, bonusPoints } = this.exercise; if (maxPoints) { From 2183a604a35704c4568b3bf00e272f92f30e7c78 Mon Sep 17 00:00:00 2001 From: Arda Karaman <47991675+ardakaraman0@users.noreply.github.com> Date: Sun, 12 Jan 2025 11:47:30 +0100 Subject: [PATCH 09/16] Adaptive learning: Improve terminology of competency relations (#10031) --- src/main/webapp/i18n/en/competency.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/webapp/i18n/en/competency.json b/src/main/webapp/i18n/en/competency.json index 77a61fac2692..d5991a873561 100644 --- a/src/main/webapp/i18n/en/competency.json +++ b/src/main/webapp/i18n/en/competency.json @@ -161,13 +161,13 @@ "selfRelation": "You can not create a relation between a competency and itself.", "relationAlreadyExists": "This relation already exists.", "type": { - "ASSUMES": "Assumes", + "REQUIRES": "Requires", "EXTENDS": "Extends", "MATCHES": "Matches" }, "typeExplanation": { - "ASSUMES": "The head competency assumes that the student mastered the tail competency and teaches new knowledge that builds upon the tail competency.", - "EXTENDS": "The head competency assumes that the student mastered the tail competency and deepens that knowledge further.", + "REQUIRES": "The head competency requires the student to master the tail competency and teaches new knowledge that builds upon the tail competency.", + "EXTENDS": "The head competency requires the student to master the tail competency and deepens that knowledge further.", "MATCHES": "The head and tail competency match and cover the same knowledge." } } @@ -254,7 +254,7 @@ "relations": { "modalTitle": "Course competency relations", "relationTypes": { - "ASSUMES": "Assumes", + "REQUIRES": "Requires", "EXTENDS": "Extends", "MATCHES": "Matches" }, @@ -300,21 +300,21 @@ "paragraph5": "In addition to creating competencies or prerequisites yourself, you can also import them from other courses or utilize standardized competencies. For more information on this process, please refer to the user manual for Artemis." }, "courseCompetencyRelations": { - "title": "What are course competency relations?", - "paragraph1": "Course competencies can build upon or depend on one another, which is why you can establish different types of relationships between them. Competencies can either MATCH, EXTEND, or ASSUME each other. Detailed descriptions of these relationships are provided below.", + "title": "What are course competency relationships?", + "paragraph1": "Course competencies can support or rely on one another, which is why you can establish different types of relationships between them. Competencies can either MATCH, EXTEND, or REQUIRE each other. Detailed descriptions of these relationships are provided below.", "paragraph2": "In summary:", - "paragraph3": "MATCH is about equivalency between two skills.
    EXTEND is about building on an existing skill.
    ASSUME means one skill is a prerequisite for another.", + "paragraph3": "MATCH is about equivalency between two skills.
    EXTEND is about building on an existing skill.
    REQUIRE means one skill is a prerequisite for another.", "paragraph4": "To create these relationships, open the 'Edit Relations' window. In this window, you can either select the related competencies or click on each one consecutively. After that, choose the desired relation type and create the relationship. To update or delete a relation, simply click on the specific relation and either update the relation type in the form or delete it.", "relationTitle1": "1. MATCH", "relationParagraph1": "This relationship implies that two competencies are at the same level, covering the same skills and requirements. If you have one, you already have the other, and they can be considered interchangeable.", "exampleLabel": "Example:", - "relationParagraph2": "Competency A: Balancing on a stationary bike. Competency B: Standing on one leg (balancing without moving). Here, balancing on a stationary bike might \"MATCH\" balancing on one leg because both involve the same fundamental skill of balance. If you can do one, you're very likely able to do the other, as the core competency (balance) is the same in both scenarios.", + "relationParagraph2": "Competency A: Balancing on a stationary bike. Competency B: Standing on one leg (balancing without moving). Here, balancing on a stationary bike might \"MATCH\" with balancing on one leg because both involve the same fundamental skill of balance. If you can do one, you're very likely able to do the other, as the core competency (balance) is the same in both scenarios.", "relationTitle2": "2. EXTEND", - "relationParagraph3": "This relationship means one competency builds upon another by adding more complexity or breadth. In other words, once you have mastered one competency, you can move on to mastering the extended one, which takes that skill further.", - "relationParagraph4": "Competency A: Riding a bike in a straight line. Competency B: Riding a bike around curves and turns. Riding in a straight line is the foundational skill that \"EXTENDS\" into riding around curves and turns. Competency B extends Competency A by adding the need to navigate changes in direction while maintaining balance and control. Once you can ride in a straight line, you're ready to extend that ability to more complex riding scenarios.", - "relationTitle3": "3. ASSUME", + "relationParagraph3": "This relationship means one competency adds complexity or expands on another. In other words, once you have mastered one competency, you can move on to mastering the extended one, which takes that skill further.", + "relationParagraph4": "Competency A: Riding a bike in a straight line. Competency B: Riding a bike around curves and turns. Riding in a straight line is the foundational skill that \"EXTENDS\" into riding around curves and turns. Competency B builds upon Competency A by adding the need to navigate changes in direction while maintaining balance and control. Once you can ride in a straight line, you're ready to extend that ability to more complex riding scenarios.", + "relationTitle3": "3. REQUIRES", "relationParagraph5": "This relationship implies that one competency relies on another being already mastered. To perform the more advanced competency, the learner must first have the underlying skill or knowledge.", - "relationParagraph6": "Competency A: Riding a bike with confidence on flat terrain. Competency B: Riding a bike uphill. Riding a bike uphill ASSUMES that the learner has already mastered riding on flat terrain. The skill of riding on flat ground is a prerequisite because uphill riding adds more resistance and requires additional strength and technique. Without mastering the basic competency of flat-ground riding, the learner wouldn't be ready to tackle riding uphill." + "relationParagraph6": "Competency A: Riding a bike with confidence on flat terrain. Competency B: Riding a bike uphill. Riding a bike uphill \"REQUIRES\" that the learner has already mastered riding on flat terrain. The skill of riding on flat ground is a prerequisite because uphill riding adds more resistance and requires additional strength and technique. Without mastering the basic competency of flat-ground riding, the learner wouldn't be ready to tackle riding uphill." } } }, From fda9e65a8b5345fa3a9cf6e8ce8b17ad71cf61aa Mon Sep 17 00:00:00 2001 From: Leon Laurin Wehrhahn <58460654+LeonWehrhahn@users.noreply.github.com> Date: Sun, 12 Jan 2025 13:29:16 +0100 Subject: [PATCH 10/16] Assessment: Do not display automatic as manual feedback in the online code editor (#10120) --- .../file-upload-submission.component.ts | 4 +-- .../modeling-submission.component.ts | 4 +-- ...code-editor-student-container.component.ts | 4 +-- .../exercises/shared/result/result.utils.ts | 19 ++++++++----- .../text/participate/text-editor.component.ts | 4 +-- .../ParticipationIntegrationTest.java | 2 +- ...modeling-submission-team.component.spec.ts | 2 +- .../modeling-submission.component.spec.ts | 2 +- .../spec/component/utils/result.utils.spec.ts | 27 ++++++++++++++++--- 9 files changed, 46 insertions(+), 22 deletions(-) diff --git a/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts b/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts index ebd49234be13..2157ebc343b1 100644 --- a/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts +++ b/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts @@ -22,7 +22,7 @@ import { ButtonType } from 'app/shared/components/button.component'; import { Result } from 'app/entities/result.model'; import { AccountService } from 'app/core/auth/account.service'; import { getFirstResultWithComplaint, getLatestSubmissionResult } from 'app/entities/submission.model'; -import { addParticipationToResult, getUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; +import { addParticipationToResult, getManualUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; import { Feedback, checkSubsequentFeedbackInAssessment } from 'app/entities/feedback.model'; import { onError } from 'app/shared/util/global.utils'; import { getCourseFromExercise } from 'app/entities/exercise.model'; @@ -247,7 +247,7 @@ export class FileUploadSubmissionComponent implements OnInit, ComponentCanDeacti get unreferencedFeedback(): Feedback[] | undefined { if (this.result?.feedbacks) { checkSubsequentFeedbackInAssessment(this.result.feedbacks); - return getUnreferencedFeedback(this.result.feedbacks); + return getManualUnreferencedFeedback(this.result.feedbacks); } return undefined; } diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index b77b05f098dd..3c0e259e7707 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -15,7 +15,7 @@ import { ModelingAssessmentService } from 'app/exercises/modeling/assess/modelin import { ModelingSubmissionService } from 'app/exercises/modeling/participate/modeling-submission.service'; import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; import { getExerciseDueDate, hasExerciseDueDatePassed } from 'app/exercises/shared/exercise/exercise.utils'; -import { addParticipationToResult, getUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; +import { addParticipationToResult, getAutomaticUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; import { AccountService } from 'app/core/auth/account.service'; import { GuidedTourService } from 'app/guided-tour/guided-tour.service'; import { modelingTour } from 'app/guided-tour/tours/modeling-tour'; @@ -664,7 +664,7 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component get unreferencedFeedback(): Feedback[] | undefined { if (this.assessmentResult?.feedbacks) { checkSubsequentFeedbackInAssessment(this.assessmentResult.feedbacks); - return getUnreferencedFeedback(this.assessmentResult.feedbacks); + return getAutomaticUnreferencedFeedback(this.assessmentResult.feedbacks); } return undefined; } diff --git a/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts b/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts index 697507764dac..bb9a508331d6 100644 --- a/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts +++ b/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts @@ -14,7 +14,7 @@ import { DomainType } from 'app/exercises/programming/shared/code-editor/model/c import { ActivatedRoute } from '@angular/router'; import { CodeEditorContainerComponent } from 'app/exercises/programming/shared/code-editor/container/code-editor-container.component'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; -import { getUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; +import { getManualUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; import { SubmissionType } from 'app/entities/submission.model'; import { SubmissionPolicyType } from 'app/entities/submission-policy.model'; import { Course } from 'app/entities/course.model'; @@ -160,7 +160,7 @@ export class CodeEditorStudentContainerComponent implements OnInit, OnDestroy { get unreferencedFeedback(): Feedback[] { if (this.latestResult?.feedbacks) { checkSubsequentFeedbackInAssessment(this.latestResult.feedbacks); - return getUnreferencedFeedback(this.latestResult.feedbacks) ?? []; + return getManualUnreferencedFeedback(this.latestResult.feedbacks) ?? []; } return []; } diff --git a/src/main/webapp/app/exercises/shared/result/result.utils.ts b/src/main/webapp/app/exercises/shared/result/result.utils.ts index c77cc3a9fcf2..85b30f694334 100644 --- a/src/main/webapp/app/exercises/shared/result/result.utils.ts +++ b/src/main/webapp/app/exercises/shared/result/result.utils.ts @@ -116,16 +116,21 @@ export const addParticipationToResult = (result: Result | undefined, participati }; /** - * searches for all unreferenced feedback in an array of feedbacks of a result + * searches for all manual unreferenced feedback in an array of feedbacks of a result * @param feedbacks the feedback of a result * @returns an array with the unreferenced feedback of the result */ -export const getUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { - return feedbacks - ? feedbacks.filter( - (feedbackElement) => !feedbackElement.reference && (feedbackElement.type === FeedbackType.MANUAL_UNREFERENCED || feedbackElement.type === FeedbackType.AUTOMATIC), - ) - : undefined; +export const getManualUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { + return feedbacks ? feedbacks.filter((feedbackElement) => !feedbackElement.reference && feedbackElement.type === FeedbackType.MANUAL_UNREFERENCED) : undefined; +}; + +/** + * searches for all automatic unreferenced feedback in an array of feedbacks of a result + * @param feedbacks the feedback of a result + * @returns an array with the unreferenced feedback of the result + */ +export const getAutomaticUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { + return feedbacks ? feedbacks.filter((feedbackElement) => !feedbackElement.reference && feedbackElement.type === FeedbackType.AUTOMATIC) : undefined; }; export function isAIResultAndFailed(result: Result | undefined): boolean { diff --git a/src/main/webapp/app/exercises/text/participate/text-editor.component.ts b/src/main/webapp/app/exercises/text/participate/text-editor.component.ts index f45eae358ac0..7e556f44aeac 100644 --- a/src/main/webapp/app/exercises/text/participate/text-editor.component.ts +++ b/src/main/webapp/app/exercises/text/participate/text-editor.component.ts @@ -20,7 +20,7 @@ import { TextSubmission } from 'app/entities/text/text-submission.model'; import { StringCountService } from 'app/exercises/text/participate/string-count.service'; import { AccountService } from 'app/core/auth/account.service'; import { getFirstResultWithComplaint, getLatestSubmissionResult, setLatestSubmissionResult } from 'app/entities/submission.model'; -import { getUnreferencedFeedback, isAthenaAIResult } from 'app/exercises/shared/result/result.utils'; +import { getManualUnreferencedFeedback, isAthenaAIResult } from 'app/exercises/shared/result/result.utils'; import { onError } from 'app/shared/util/global.utils'; import { Course } from 'app/entities/course.model'; import { getCourseFromExercise } from 'app/entities/exercise.model'; @@ -312,7 +312,7 @@ export class TextEditorComponent implements OnInit, OnDestroy, ComponentCanDeact * Check whether or not a result exists and if, returns the unreferenced feedback of it */ get unreferencedFeedback(): Feedback[] | undefined { - return this.result ? getUnreferencedFeedback(this.result.feedbacks) : undefined; + return this.result ? getManualUnreferencedFeedback(this.result.feedbacks) : undefined; } get wordCount(): number { diff --git a/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java index bbe46c5b9b19..a2ee371bde2f 100644 --- a/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java @@ -802,7 +802,7 @@ void requestModelingFeedbackSuccess_withAthenaFailure() throws Exception { request.putWithResponseBody("/api/exercises/" + modelingExercise.getId() + "/request-feedback", null, StudentParticipation.class, HttpStatus.OK); - verify(resultWebsocketService, timeout(2000).times(1)).broadcastNewResult(any(), resultCaptor.capture()); + verify(resultWebsocketService, timeout(2000).times(2)).broadcastNewResult(any(), resultCaptor.capture()); Result invokedModelingResult = resultCaptor.getAllValues().getFirst(); assertThat(invokedModelingResult).isNotNull(); diff --git a/src/test/javascript/spec/component/modeling-submission/modeling-submission-team.component.spec.ts b/src/test/javascript/spec/component/modeling-submission/modeling-submission-team.component.spec.ts index 1ebae9d40337..2a1eeea28e7d 100644 --- a/src/test/javascript/spec/component/modeling-submission/modeling-submission-team.component.spec.ts +++ b/src/test/javascript/spec/component/modeling-submission/modeling-submission-team.component.spec.ts @@ -505,7 +505,7 @@ describe('ModelingSubmissionComponent', () => { detailText: 'feedback1', credits: 1, gradingInstruction, - type: FeedbackType.MANUAL_UNREFERENCED, + type: FeedbackType.AUTOMATIC, } as Feedback, { id: 2, diff --git a/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts b/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts index 5117ae0429b7..603e093efca5 100644 --- a/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts +++ b/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts @@ -706,7 +706,7 @@ describe('ModelingSubmissionComponent', () => { detailText: 'feedback1', credits: 1, gradingInstruction, - type: FeedbackType.MANUAL_UNREFERENCED, + type: FeedbackType.AUTOMATIC, } as Feedback, { id: 2, diff --git a/src/test/javascript/spec/component/utils/result.utils.spec.ts b/src/test/javascript/spec/component/utils/result.utils.spec.ts index d8c0a7f4c0e4..9b4463b3c353 100644 --- a/src/test/javascript/spec/component/utils/result.utils.spec.ts +++ b/src/test/javascript/spec/component/utils/result.utils.spec.ts @@ -1,9 +1,10 @@ import { ResultTemplateStatus, breakCircularResultBackReferences, + getAutomaticUnreferencedFeedback, + getManualUnreferencedFeedback, getResultIconClass, getTextColorClass, - getUnreferencedFeedback, isOnlyCompilationTested, } from 'app/exercises/shared/result/result.utils'; import { Feedback, FeedbackType, STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER } from 'app/entities/feedback.model'; @@ -18,12 +19,30 @@ import { Result } from 'app/entities/result.model'; import dayjs from 'dayjs/esm'; describe('ResultUtils', () => { - it('should filter out all non unreferenced feedbacks', () => { - const feedbacks = [{ reference: 'foo' }, { reference: 'foo', type: FeedbackType.MANUAL_UNREFERENCED }, { type: FeedbackType.MANUAL_UNREFERENCED }, {}]; - const unreferencedFeedbacks = getUnreferencedFeedback(feedbacks); + it('should filter out all non unreferenced feedbacks that do not have type MANUAL_UNREFERENCED', () => { + const feedbacks = [ + { reference: 'foo' }, + { reference: 'foo', type: FeedbackType.MANUAL_UNREFERENCED }, + { type: FeedbackType.AUTOMATIC }, + { type: FeedbackType.MANUAL_UNREFERENCED }, + {}, + ]; + const unreferencedFeedbacks = getManualUnreferencedFeedback(feedbacks); expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.MANUAL_UNREFERENCED }]); }); + it('should filter out all non unreferenced feedbacks that do not have type AUTOMATIC', () => { + const feedbacks = [ + { reference: 'foo' }, + { reference: 'foo', type: FeedbackType.AUTOMATIC }, + { type: FeedbackType.AUTOMATIC }, + { type: FeedbackType.MANUAL_UNREFERENCED }, + {}, + ]; + const unreferencedFeedbacks = getAutomaticUnreferencedFeedback(feedbacks); + expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.AUTOMATIC }]); + }); + it.each([ { result: { From 0646b251041be24799f90f2abd38b83d4b26e79e Mon Sep 17 00:00:00 2001 From: Murad Talibov <56686446+muradium@users.noreply.github.com> Date: Sun, 12 Jan 2025 18:38:37 +0100 Subject: [PATCH 11/16] Modeling exercises: Fix manual feedback results not being shown (#10132) --- .../participate/modeling-submission.component.ts | 4 ++-- .../webapp/app/exercises/shared/result/result.utils.ts | 10 +++++++--- .../spec/component/utils/result.utils.spec.ts | 8 ++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index 3c0e259e7707..b77b05f098dd 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -15,7 +15,7 @@ import { ModelingAssessmentService } from 'app/exercises/modeling/assess/modelin import { ModelingSubmissionService } from 'app/exercises/modeling/participate/modeling-submission.service'; import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; import { getExerciseDueDate, hasExerciseDueDatePassed } from 'app/exercises/shared/exercise/exercise.utils'; -import { addParticipationToResult, getAutomaticUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; +import { addParticipationToResult, getUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; import { AccountService } from 'app/core/auth/account.service'; import { GuidedTourService } from 'app/guided-tour/guided-tour.service'; import { modelingTour } from 'app/guided-tour/tours/modeling-tour'; @@ -664,7 +664,7 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component get unreferencedFeedback(): Feedback[] | undefined { if (this.assessmentResult?.feedbacks) { checkSubsequentFeedbackInAssessment(this.assessmentResult.feedbacks); - return getAutomaticUnreferencedFeedback(this.assessmentResult.feedbacks); + return getUnreferencedFeedback(this.assessmentResult.feedbacks); } return undefined; } diff --git a/src/main/webapp/app/exercises/shared/result/result.utils.ts b/src/main/webapp/app/exercises/shared/result/result.utils.ts index 85b30f694334..4ea2f86dfae7 100644 --- a/src/main/webapp/app/exercises/shared/result/result.utils.ts +++ b/src/main/webapp/app/exercises/shared/result/result.utils.ts @@ -125,12 +125,16 @@ export const getManualUnreferencedFeedback = (feedbacks: Feedback[] | undefined) }; /** - * searches for all automatic unreferenced feedback in an array of feedbacks of a result + * searches for all unreferenced feedback in an array of feedbacks of a result * @param feedbacks the feedback of a result * @returns an array with the unreferenced feedback of the result */ -export const getAutomaticUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { - return feedbacks ? feedbacks.filter((feedbackElement) => !feedbackElement.reference && feedbackElement.type === FeedbackType.AUTOMATIC) : undefined; +export const getUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { + return feedbacks + ? feedbacks.filter( + (feedbackElement) => !feedbackElement.reference && (feedbackElement.type === FeedbackType.MANUAL_UNREFERENCED || feedbackElement.type === FeedbackType.AUTOMATIC), + ) + : undefined; }; export function isAIResultAndFailed(result: Result | undefined): boolean { diff --git a/src/test/javascript/spec/component/utils/result.utils.spec.ts b/src/test/javascript/spec/component/utils/result.utils.spec.ts index 9b4463b3c353..b7160cefa41c 100644 --- a/src/test/javascript/spec/component/utils/result.utils.spec.ts +++ b/src/test/javascript/spec/component/utils/result.utils.spec.ts @@ -1,10 +1,10 @@ import { ResultTemplateStatus, breakCircularResultBackReferences, - getAutomaticUnreferencedFeedback, getManualUnreferencedFeedback, getResultIconClass, getTextColorClass, + getUnreferencedFeedback, isOnlyCompilationTested, } from 'app/exercises/shared/result/result.utils'; import { Feedback, FeedbackType, STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER } from 'app/entities/feedback.model'; @@ -31,7 +31,7 @@ describe('ResultUtils', () => { expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.MANUAL_UNREFERENCED }]); }); - it('should filter out all non unreferenced feedbacks that do not have type AUTOMATIC', () => { + it('should filter out all non unreferenced feedbacks', () => { const feedbacks = [ { reference: 'foo' }, { reference: 'foo', type: FeedbackType.AUTOMATIC }, @@ -39,8 +39,8 @@ describe('ResultUtils', () => { { type: FeedbackType.MANUAL_UNREFERENCED }, {}, ]; - const unreferencedFeedbacks = getAutomaticUnreferencedFeedback(feedbacks); - expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.AUTOMATIC }]); + const unreferencedFeedbacks = getUnreferencedFeedback(feedbacks); + expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.AUTOMATIC }, { type: FeedbackType.MANUAL_UNREFERENCED }]); }); it.each([ From 43d0089641a6435a16f7d62123d79a01b5dd25a9 Mon Sep 17 00:00:00 2001 From: Stephan Krusche Date: Mon, 13 Jan 2025 22:52:52 +0100 Subject: [PATCH 12/16] Development: Update to Angular 19, standalone and inject() (#10112) --- angular.json | 4 +- eslint.config.js | 16 - jest.config.js | 8 +- package-lock.json | 4617 ++++++++++------- package.json | 117 +- patches/ng-mocks+14.13.1.patch | 16 + proxy.conf.mjs | 23 +- .../account/activate/activate.component.ts | 1 - ...nal-user-password-reset-modal.component.ts | 1 - .../finish/password-reset-finish.component.ts | 1 - .../init/password-reset-init.component.ts | 1 - .../password-strength-bar.component.ts | 1 - .../account/password/password.component.ts | 1 - .../account/register/register.component.ts | 1 - .../account/settings/settings.component.ts | 1 - src/main/webapp/app/admin/admin.module.ts | 4 +- src/main/webapp/app/admin/admin.route.ts | 50 +- .../app/admin/audits/audits.component.ts | 23 +- .../webapp/app/admin/audits/audits.service.ts | 4 +- .../cleanup-operation-modal.component.ts | 1 - .../cleanup-service.component.ts | 1 - .../configuration/configuration.component.ts | 13 +- .../configuration/configuration.service.ts | 4 +- .../admin-feature-toggle.component.ts | 8 +- .../admin/health/health-modal.component.ts | 10 +- .../app/admin/health/health.component.ts | 16 +- .../webapp/app/admin/health/health.service.ts | 13 +- .../legal-document-update-routing.module.ts | 3 +- .../legal/legal-document-update.component.ts | 23 +- .../app/admin/legal/legal-update.module.ts | 2 +- ...document-unsaved-changes-warning.module.ts | 3 +- .../unsaved-changes-warning.component.ts | 10 +- .../webapp/app/admin/logs/logs.component.ts | 13 +- .../webapp/app/admin/logs/logs.service.ts | 4 +- .../edit-lti-configuration.component.ts | 6 +- .../lti-configuration.component.ts | 31 +- .../lti-configuration.route.ts | 9 +- .../blocks/jvm-memory/jvm-memory.component.ts | 1 - .../jvm-threads/jvm-threads.component.ts | 1 - .../metrics-cache/metrics-cache.component.ts | 1 - .../metrics-datasource.component.ts | 1 - .../metrics-endpoints-requests.component.ts | 1 - .../metrics-garbagecollector.component.ts | 1 - .../metrics-modal-threads.component.ts | 9 +- .../metrics-request.component.ts | 1 - .../metrics-system.component.ts | 1 - .../app/admin/metrics/metrics.component.ts | 1 - .../organization-count-dto.model.ts | 2 - ...rganization-management-detail.component.ts | 21 +- ...organization-management-resolve.service.ts | 4 +- ...rganization-management-update.component.ts | 15 +- .../organization-management.component.ts | 11 +- .../organization-management.route.ts | 13 +- .../organization-management.service.ts | 10 +- ...ort-standardized-competencies.component.ts | 14 +- .../knowledge-area-edit.component.ts | 17 +- .../standardized-competency-edit.component.ts | 26 +- ...ardized-competency-management.component.ts | 45 +- .../admin/statistics/statistics.component.ts | 6 +- ...otification-management-detail.component.ts | 18 +- ...notification-management-resolve.service.ts | 4 +- ...otification-management-update.component.ts | 21 +- ...ystem-notification-management.component.ts | 33 +- .../system-notification-management.route.ts | 17 +- .../upcoming-exams-and-exercises.component.ts | 16 +- .../delete-users-button.component.ts | 13 +- .../user-management-detail.component.ts | 13 +- .../user-management-resolve.service.ts | 4 +- .../user-management-update.component.ts | 61 +- .../user-management.component.ts | 63 +- .../user-management/user-management.route.ts | 12 +- src/main/webapp/app/app-routing.module.ts | 198 - ...main.component.html => app.component.html} | 0 ...main.component.scss => app.component.scss} | 0 .../main.component.ts => app.component.ts} | 57 +- src/main/webapp/app/app.config.ts | 128 + src/main/webapp/app/app.main.ts | 46 +- src/main/webapp/app/app.module.ts | 68 - src/main/webapp/app/app.routes.ts | 185 + .../assessment-complaint-alert.component.ts | 2 + .../assessment-header.component.ts | 22 +- .../assessment-instructions.module.ts | 2 - .../assessment-instructions.component.html | 4 +- .../assessment-instructions.component.ts | 27 +- ...sable-assessment-instructions.component.ts | 4 + .../expandable-section.component.ts | 10 +- .../assessment-layout.component.ts | 5 + .../assessment-locks.component.ts | 32 +- .../assessment-locks.route.ts | 6 +- .../assessment-note.component.ts | 2 + .../assessment/assessment-shared.module.ts | 2 - .../assessment-warning.component.ts | 5 +- .../webapp/app/assessment/athena.service.ts | 10 +- ...nstructions-assessment-layout.component.ts | 6 + ...ssment-correction-round-badge.component.ts | 3 + .../unreferenced-feedback-detail.component.ts | 29 +- .../complaints-student-view.component.ts | 1 - .../complaints-for-tutor.component.ts | 1 - .../form/complaints-form.component.ts | 1 - .../list-of-complaints.component.ts | 1 - .../list-of-complaints.route.ts | 9 +- .../request/complaint-request.component.ts | 1 - .../response/complaint-response.component.ts | 1 - .../app/core/about-us/about-us.component.ts | 15 +- .../core/about-us/artemis-about-us.module.ts | 7 +- src/main/webapp/app/core/core.module.ts | 117 - .../artemis-version.interceptor.ts | 18 +- .../interceptor/auth-expired.interceptor.ts | 12 +- ...browser-fingerprint.interceptor.service.ts | 8 +- .../interceptor/errorhandler.interceptor.ts | 8 +- .../interceptor/notification.interceptor.ts | 4 +- .../app/core/language/language.helper.ts | 19 +- ...ta-export-confirmation-dialog.component.ts | 17 +- ...data-export-confirmation-dialog.service.ts | 10 +- .../data-export-request-button.directive.ts | 14 +- .../data-export/data-export.component.ts | 20 +- .../legal/data-export/data-export.service.ts | 4 +- .../app/core/legal/imprint-routing.module.ts | 6 +- .../app/core/legal/imprint.component.ts | 14 +- .../webapp/app/core/legal/imprint.module.ts | 3 +- .../app/core/legal/privacy-routing.module.ts | 10 +- .../app/core/legal/privacy.component.ts | 19 +- .../webapp/app/core/legal/privacy.module.ts | 14 +- .../app/core/sentry/sentry.error-handler.ts | 4 - .../app/core/theme/theme-switch.component.ts | 1 - .../app/core/user/admin-user.service.ts | 6 +- src/main/webapp/app/core/user/user.service.ts | 6 +- .../webapp/app/core/util/alert.service.ts | 21 +- .../competencies-popover.component.ts | 7 +- .../competency-accordion.component.ts | 26 +- .../competency-card.component.ts | 16 +- .../competency-management-table.component.ts | 8 +- .../competency-management.component.ts | 1 - .../import-all-competencies.component.ts | 16 +- .../competency-rings.component.ts | 4 + .../course/competencies/competency.module.ts | 2 - .../course/competencies/competency.service.ts | 9 +- ...e-competencies-relation-graph.component.ts | 32 +- ...e-competencies-relation-modal.component.ts | 6 +- ...-competency-explanation-modal.component.ts | 4 +- ...urse-competency-relation-form.component.ts | 3 +- ...urse-competency-relation-node.component.ts | 1 - ...all-course-competencies-modal.component.ts | 6 +- ...-course-competencies-settings.component.ts | 3 +- .../competencies/course-competency.service.ts | 14 +- .../create/create-competency.component.ts | 19 +- .../create-course-competency.component.ts | 18 +- .../create/create-prerequisite.component.ts | 18 +- .../edit/edit-competency.component.ts | 18 +- .../edit/edit-course-competency.component.ts | 18 +- .../edit/edit-prerequisite.component.ts | 18 +- ...common-course-competency-form.component.ts | 57 +- .../competency/competency-form.component.ts | 25 +- .../forms/course-competency-form.component.ts | 47 +- .../prerequisite-form.component.ts | 25 +- ...petency-recommendation-detail.component.ts | 20 +- .../course-description-form.component.ts | 14 +- .../generate-competencies.component.ts | 35 +- ...ort-standardized-competencies.component.ts | 20 +- ...ndardized-course-competencies.component.ts | 24 +- ...rt-standardized-prerequisites.component.ts | 26 +- .../import/competency-search.component.ts | 6 + .../import-competencies-table.component.ts | 8 + .../import/import-competencies.component.ts | 1 - .../import-course-competencies.component.ts | 6 +- .../import/import-prerequisites.component.ts | 1 - .../judgement-of-learning-rating.component.ts | 11 +- .../competencies/prerequisite.service.ts | 9 +- .../taxonomy-select.component.ts | 5 +- .../course/course-access-storage.service.ts | 6 +- .../course-for-import-dto-paging-service.ts | 6 +- .../course-scores-routing.module.ts | 7 +- .../course-scores/course-scores.component.ts | 49 +- .../course-scores/course-scores.module.ts | 3 +- ...essment-dashboard-information.component.ts | 5 +- .../assessment-dashboard.component.ts | 1 - .../assessment-dashboard.route.ts | 7 +- .../exam-assessment-buttons.component.ts | 1 - .../course/dashboards/due-date-stat.model.ts | 2 - .../dashboards/stats-for-dashboard.model.ts | 2 - .../competency-graph-modal.component.ts | 12 +- .../competency-graph.component.ts | 3 +- .../competency-node.component.ts | 1 - .../learning-path-exercise.component.ts | 1 - .../learning-path-lecture-unit.component.ts | 12 +- ...nav-overview-learning-objects.component.ts | 10 +- .../learning-path-nav-overview.component.ts | 12 +- .../learning-path-student-nav.component.ts | 12 +- .../learning-paths-analytics.component.ts | 12 +- .../learning-paths-configuration.component.ts | 12 +- .../learning-paths-state.component.ts | 12 +- .../learning-paths-table.component.ts | 14 +- ...learning-path-instructor-page.component.ts | 15 +- .../learning-path-student-page.component.ts | 12 +- .../app/course/manage/course-admin.service.ts | 10 +- .../manage/course-exercise-card.component.ts | 4 + .../course/manage/course-for-dashboard-dto.ts | 3 - .../course-group-membership.component.ts | 23 +- .../course-lti-configuration.component.ts | 9 +- ...edit-course-lti-configuration.component.ts | 12 +- ...course-management-detail-view-dto.model.ts | 2 - ...e-management-exercises-search.component.ts | 4 + ...course-management-exercises.component.html | 71 +- .../course-management-exercises.component.ts | 39 +- .../course-management-resolve.service.ts | 4 +- .../course-management-statistics-dto.ts | 2 - .../course-management-statistics.component.ts | 16 +- .../course-management-tab-bar.component.ts | 48 +- .../manage/course-management.component.ts | 35 +- .../course/manage/course-management.module.ts | 8 +- .../course/manage/course-management.route.ts | 104 +- .../manage/course-management.service.ts | 22 +- .../course/manage/course-update.component.ts | 86 +- .../manage/courses-for-dashboard-dto.ts | 2 - .../course-detail-doughnut-chart.component.ts | 16 +- .../course-detail-line-chart.component.ts | 20 +- .../manage/detail/course-detail.component.ts | 30 +- .../manage/image-cropper-modal.component.ts | 9 +- .../course-management-card.component.ts | 26 + ...ourse-management-exercise-row.component.ts | 24 +- ...-overview-exercise-statistics-dto.model.ts | 2 - ...anagement-overview-statistics-dto.model.ts | 2 - ...anagement-overview-statistics.component.ts | 13 +- ...m-case-instructor-detail-view.component.ts | 65 +- ...giarism-cases-instructor-view.component.ts | 31 +- ...plagiarism-cases-instructor-view.module.ts | 13 +- .../shared/plagiarism-cases-shared.module.ts | 11 +- .../shared/plagiarism-cases.service.ts | 6 +- .../shared/plagiarism-results.service.ts | 6 +- .../plagiarism-case-review.component.ts | 4 + .../plagiarism-case-verdict.component.ts | 4 + ...rism-case-student-detail-view.component.ts | 21 +- .../plagiarism-cases-student-view.module.ts | 7 +- .../shared/meeting-pattern.pipe.ts | 5 +- .../shared/remove-seconds.pipe.ts | 4 +- .../tutorial-group-detail.component.ts | 36 +- ...rial-group-free-days-overview.component.ts | 15 +- .../tutorial-group-session-row.component.ts | 21 +- ...tutorial-group-sessions-table.component.ts | 14 +- ...l-group-utilization-indicator.component.ts | 3 + .../shared/tutorial-groups-shared.module.ts | 11 +- .../tutorial-group-row.component.ts | 9 +- .../tutorial-groups-table.component.ts | 20 +- .../registered-students.component.ts | 22 +- ...te-tutorial-group-free-period.component.ts | 16 +- ...it-tutorial-group-free-period.component.ts | 15 +- ...torial-group-free-period-form.component.ts | 34 +- ...group-free-period-row-buttons.component.ts | 15 +- ...group-free-periods-management.component.ts | 25 +- ...rial-group-free-periods-table.component.ts | 6 + ...torial-group-management-resolve.service.ts | 8 +- ...create-tutorial-group-session.component.ts | 16 +- .../edit-tutorial-group-session.component.ts | 16 +- .../tutorial-group-session-form.component.ts | 18 +- .../cancellation-modal.component.ts | 19 +- ...ial-group-session-row-buttons.component.ts | 16 +- ...ial-group-sessions-management.component.ts | 32 +- .../tutorial-groups-checklist.component.ts | 22 +- ...tutorial-groups-configuration.component.ts | 25 +- ...tutorial-groups-configuration.component.ts | 23 +- ...ial-groups-configuration-form.component.ts | 15 +- .../tutorial-groups-management.module.ts | 2 - .../tutorial-groups-management.route.ts | 42 +- .../create-tutorial-group.component.ts | 17 +- .../edit-tutorial-group.component.ts | 21 +- .../schedule-form/schedule-form.component.ts | 23 +- .../tutorial-group-form.component.html | 2 +- .../tutorial-group-form.component.ts | 23 +- ...orial-group-management-detail.component.ts | 20 +- .../tutorial-group-row-buttons.component.ts | 15 +- ...ial-groups-course-information.component.ts | 4 + ...tutorial-groups-export-button.component.ts | 17 +- ...tutorial-groups-import-button.component.ts | 10 +- ...ps-registration-import-dialog.component.ts | 24 +- .../tutorial-groups-management.component.ts | 48 +- .../boolean-detail.component.ts | 4 +- .../date-detail/date-detail.component.ts | 1 - .../link-detail/link-detail.component.ts | 1 - ...ary-repository-buttons-detail.component.ts | 4 +- ...rogramming-diff-report-detail.component.ts | 1 - ...ing-repository-buttons-detail.component.ts | 1 - ...rogramming-test-status-detail.component.ts | 1 - .../text-detail/text-detail.component.ts | 1 - .../detail-overview-list.component.html | 2 +- .../detail-overview-list.component.ts | 30 + .../app/detail-overview-list/detail.module.ts | 2 +- .../exercise-detail.directive.ts | 7 +- src/main/webapp/app/entities/bonus.model.ts | 4 - .../webapp/app/entities/competency.model.ts | 6 - .../competency/learning-path.model.ts | 2 - .../app/entities/complaint-response.model.ts | 2 - .../webapp/app/entities/complaint.model.ts | 2 - src/main/webapp/app/entities/course.model.ts | 2 - .../app/entities/example-submission.model.ts | 2 - src/main/webapp/app/entities/lecture.model.ts | 2 - .../webapp/app/entities/metis/post.model.ts | 2 - .../tutor-participation.model.ts | 2 - ...ogramming-exercise-git-diff-entry.model.ts | 2 - ...gramming-exercise-git-diff-report.model.ts | 2 - .../course-management-statistics-model.ts | 2 - .../quiz/quiz-statistic-counter.model.ts | 2 - .../tutorial-group-registration.model.ts | 2 - .../exam/exam-scores/exam-score-dtos.model.ts | 12 - ...m-scores-average-scores-graph.component.ts | 5 - .../exam/exam-scores/exam-scores.component.ts | 1 - .../app/exam/exam-scores/exam-scores.route.ts | 4 +- .../manage/exam-exercise-update.service.ts | 2 - .../manage/exam-management-resolve.service.ts | 10 +- .../exam/manage/exam-management.component.ts | 33 +- .../app/exam/manage/exam-management.module.ts | 5 +- .../app/exam/manage/exam-management.route.ts | 230 +- .../exam/manage/exam-management.service.ts | 12 +- .../app/exam/manage/exam-status.component.ts | 19 +- ...ve-announcement-create-button.component.ts | 13 +- ...ive-announcement-create-modal.component.ts | 18 +- ...checklist-exercisegroup-table.component.ts | 6 + .../exam-checklist.component.ts | 38 +- .../exam-checklist.service.ts | 4 +- ...exam-edit-working-time-dialog.component.ts | 17 +- .../exam-edit-working-time.component.ts | 13 +- .../manage/exams/exam-detail.component.ts | 31 +- .../exam-exercise-import.component.ts | 9 +- .../exam-import/exam-import-paging.service.ts | 6 +- .../exam-import/exam-import.component.ts | 30 +- .../exam-mode-picker.component.ts | 3 + .../exam-mode-picker.module.ts | 3 +- .../manage/exams/exam-update.component.ts | 51 +- .../exercise-group-update.component.ts | 19 +- .../exercise-groups/exercise-group.service.ts | 10 +- .../exercise-groups.component.ts | 53 +- .../modeling-exercise-group-cell.component.ts | 2 + ...ogramming-exercise-group-cell.component.ts | 17 +- ...student-exam-detail-table-row.component.ts | 6 +- .../student-exam-detail.component.html | 2 +- .../student-exam-detail.component.ts | 38 +- .../student-exam-status.component.ts | 5 + .../student-exam-summary.component.ts | 8 +- ...rogramming-exercise-exam-diff.component.ts | 36 +- .../student-exam-timeline.component.ts | 34 +- .../student-exams/student-exam.service.ts | 12 +- .../student-exams.component.html | 2 +- .../student-exams/student-exams.component.ts | 51 +- .../students/exam-students.component.ts | 40 +- ...students-upload-images-button.component.ts | 8 +- ...students-upload-images-dialog.component.ts | 20 +- .../students-upload-images.module.ts | 3 +- ...xam-students-attendance-check.component.ts | 27 +- .../plagiarism-cases-overview.component.ts | 1 - .../suspicious-behavior.component.ts | 1 - .../suspicious-sessions-overview.component.ts | 1 - .../suspicious-sessions.component.ts | 2 - .../create-test-run-modal.component.ts | 14 +- .../test-run-management.component.html | 2 +- .../test-run-management.component.ts | 42 +- .../test-runs/test-run-ribbon.component.ts | 2 + .../exam-live-events-button.component.ts | 14 +- .../exam-live-events-overlay.component.ts | 16 +- .../events/exam-live-events.module.ts | 3 +- .../exam-bar/exam-bar.component.ts | 7 +- .../exam-participation-cover.component.ts | 29 +- .../exam-navigation-bar.component.ts | 24 +- .../exam-navigation-bar.module.ts | 3 +- .../exam-navigation-sidebar.component.ts | 34 +- .../exam-participation-live-events.service.ts | 14 +- .../exam-participation.component.ts | 78 +- .../participate/exam-participation.module.ts | 4 +- .../participate/exam-participation.route.ts | 14 +- .../participate/exam-participation.service.ts | 12 +- .../exam-start-information.component.ts | 1 - ...m-exercise-update-highlighter.component.ts | 8 +- ...exam-exercise-update-highlighter.module.ts | 3 +- .../exercises/exam-page.component.ts | 4 +- .../exam-submission-components.module.ts | 12 +- .../exam-exercise-overview-page.component.ts | 21 +- .../exercise-save-button.component.ts | 3 +- .../file-upload-exam-submission.component.ts | 48 +- .../modeling-exam-submission.component.ts | 36 +- .../programming-exam-submission.component.ts | 40 +- .../quiz/quiz-exam-submission.component.ts | 41 +- .../text/text-exam-submission.component.ts | 49 +- .../exam-general-information.component.html | 2 +- .../exam-general-information.component.ts | 13 +- .../summary/collapsible-card.component.ts | 3 + .../exam-result-summary.component.html | 2 +- .../summary/exam-result-summary.component.ts | 61 +- .../summary/exam-result-summary.module.ts | 2 - .../file-upload-exam-summary.component.ts | 9 +- ...-summary-exercise-card-header.component.ts | 5 + .../modeling-exam-summary.component.ts | 8 +- .../programming-exam-summary.component.ts | 35 +- .../quiz-exam-summary.component.ts | 32 +- .../text-exam-summary.component.ts | 6 +- .../exam-result-overview.component.ts | 34 +- .../participate/timer/exam-timer.component.ts | 9 +- .../participate/timer/exam-timer.module.ts | 3 +- .../events/exam-live-event.component.ts | 10 +- .../app/exam/shared/exam-shared.module.ts | 15 +- .../student-exam-working-time.component.ts | 2 + ... => test-exam-working-time.component.html} | 0 ...ts => test-exam-working-time.component.ts} | 8 +- .../working-time-change.component.ts | 3 + .../working-time-control.component.ts | 10 +- .../file-upload-assessment.component.ts | 58 +- .../assess/file-upload-assessment.module.ts | 2 +- .../assess/file-upload-assessment.route.ts | 6 +- .../assess/file-upload-assessment.service.ts | 6 +- .../file-upload-exercise-detail.component.ts | 24 +- ...oad-exercise-management-resolve.service.ts | 10 +- .../file-upload-exercise-management.module.ts | 8 +- .../file-upload-exercise-management.route.ts | 16 +- .../file-upload-exercise-paging.service.ts | 6 +- .../file-upload-exercise-update.component.ts | 67 +- .../manage/file-upload-exercise.component.ts | 10 + .../manage/file-upload-exercise.service.ts | 10 +- .../file-upload-participation.module.ts | 2 +- .../file-upload-participation.route.ts | 4 +- .../file-upload-submission.component.ts | 65 +- .../file-upload-submission.service.ts | 8 +- .../modeling-assessment-editor.component.ts | 55 +- .../modeling-assessment-editor.module.ts | 2 +- .../modeling-assessment-editor.route.ts | 8 +- .../assess/modeling-assessment.component.ts | 10 +- .../assess/modeling-assessment.module.ts | 3 +- .../assess/modeling-assessment.service.ts | 6 +- .../example-modeling-submission.component.ts | 38 +- .../example-modeling-submission.module.ts | 2 +- .../example-modeling-submission.route.ts | 4 +- .../modeling-exercise-detail.component.ts | 28 +- .../modeling-exercise-paging.service.ts | 6 +- .../modeling-exercise-resolver.service.ts | 10 +- .../modeling-exercise-update.component.ts | 73 +- .../manage/modeling-exercise.component.ts | 10 + .../manage/modeling-exercise.module.ts | 8 +- .../manage/modeling-exercise.route.ts | 21 +- .../manage/modeling-exercise.service.ts | 12 +- .../modeling-participation.module.ts | 5 +- .../modeling-participation.route.ts | 6 +- .../modeling-submission.component.ts | 120 +- .../modeling-submission.service.ts | 10 +- .../shared/modeling-editor.component.ts | 17 +- .../modeling/shared/modeling-editor.module.ts | 3 +- .../modeling-explanation-editor.component.ts | 27 +- .../modeling/shared/modeling.component.ts | 6 +- ...-tutor-assessment-container.component.html | 104 +- ...or-tutor-assessment-container.component.ts | 71 +- ...nt-inline-feedback-suggestion.component.ts | 13 +- ...or-assessment-inline-feedback.component.ts | 37 +- ...amming-assessment-manual-result.service.ts | 10 +- .../assess/programming-assessment.module.ts | 5 +- .../programming-manual-assessment.module.ts | 3 +- ...assessment-repo-export-button.component.ts | 8 +- ...assessment-repo-export-dialog.component.ts | 21 +- ...gramming-assessment-repo-export.service.ts | 6 +- .../git-diff-file-panel-title.component.ts | 1 - .../git-diff-file-panel.component.ts | 4 +- .../git-diff-file.component.ts | 1 - .../git-diff-line-stat.component.ts | 1 - .../git-diff-report-modal.component.ts | 28 +- .../git-diff-report.component.ts | 1 - .../manage/build-plan-editor.component.ts | 21 +- ...structor-and-editor-container.component.ts | 44 +- ...tor-instructor-base-container.component.ts | 30 +- .../code-editor-management-routing.module.ts | 25 +- .../code-editor-management.module.ts | 3 +- .../charts/category-issues-chart.component.ts | 4 +- ...a-category-distribution-chart.component.ts | 18 +- .../test-case-distribution-chart.component.ts | 16 +- ...test-case-passed-builds-chart.component.ts | 4 +- ...edback-channel-creation-modal.component.ts | 1 - ...dback-affected-students-modal.component.ts | 1 - ...feedback-detail-channel-modal.component.ts | 5 +- .../Modal/feedback-filter-modal.component.ts | 1 - .../Modal/feedback-modal.component.ts | 1 - .../feedback-analysis.component.ts | 11 +- ...ise-configure-grading-actions.component.ts | 3 + ...cise-configure-grading-status.component.ts | 5 + ...ng-exercise-configure-grading.component.ts | 71 +- ...xercise-grading-dirty-warning.component.ts | 10 +- ...-policy-configuration-actions.component.ts | 2 + ...xercise-grading-table-actions.component.ts | 9 +- .../programming-exercise-grading.module.ts | 2 - ...-exercise-grading-tasks-table.component.ts | 16 +- .../programming-exercise-task.service.ts | 12 +- .../programming-exercise-task.component.ts | 11 +- ...exercise-instruction-analysis.component.ts | 12 +- ...g-exercise-instruction-analysis.service.ts | 4 +- .../task-count-warning.component.ts | 4 + ...exercise-editable-instruction.component.ts | 31 +- ...ing-exercise-instructions-editor.module.ts | 11 +- ...mming-exercise-create-buttons.component.ts | 17 +- .../programming-exercise-detail.component.ts | 72 +- ...amming-exercise-edit-selected.component.ts | 23 +- ...ming-exercise-management-routing.module.ts | 58 +- .../programming-exercise-management.module.ts | 5 +- .../programming-exercise.component.html | 21 +- .../manage/programming-exercise.component.ts | 49 +- ...ramming-exercise-reset-button.directive.ts | 16 +- ...mming-exercise-reset-dialog.component.html | 4 +- ...ramming-exercise-reset-dialog.component.ts | 18 +- .../manage/services/build-plan.service.ts | 6 +- .../services/code-analysis-paging.service.ts | 6 +- .../programming-exercise-grading.service.ts | 10 +- .../programming-exercise-paging.service.ts | 6 +- ...gramming-exercise-participation.service.ts | 12 +- .../programming-exercise-websocket.service.ts | 6 +- .../services/programming-exercise.service.ts | 12 +- .../services/submission-policy.service.ts | 6 +- ...se-instructor-exercise-status.component.ts | 10 +- ...ng-exercise-instructor-status.component.ts | 10 +- .../programming-exercise-status.module.ts | 3 +- ...d-auxiliary-repository-button.component.ts | 2 + .../programming-exercise-update.component.ts | 55 +- .../programming-exercise-update.module.ts | 6 +- ...e-auxiliary-repository-button.component.ts | 2 + .../switch-edit-mode-button.component.ts | 1 - ...-exercise-build-configuration.component.ts | 10 +- ...cise-custom-aeolus-build-plan.component.ts | 14 +- ...ng-exercise-custom-build-plan.component.ts | 12 +- ...ogramming-exercise-difficulty.component.ts | 1 - .../programming-exercise-grading.component.ts | 40 +- ...amming-exercise-information.component.html | 2 +- ...gramming-exercise-information.component.ts | 56 +- ...programming-exercise-language.component.ts | 20 +- .../programming-exercise-mode.component.ts | 25 +- .../programming-exercise-problem.component.ts | 16 + .../programming-exercise-theia.component.ts | 12 +- ...code-editor-student-container.component.ts | 35 +- .../programming-build-run.service.ts | 6 +- ...rogramming-participation-routing.module.ts | 4 +- .../programming-participation.module.ts | 2 +- .../programming-repository-routing.module.ts | 12 +- .../programming-repository.module.ts | 7 +- ...ramming-submission-policy-status.module.ts | 3 +- .../programming-submission-policy-status.ts | 2 + .../programming-submission.service.ts | 16 +- .../programming-exercise-actions.module.ts | 7 +- ...xample-solution-repo-download.component.ts | 4 +- ...-instructor-exercise-download.component.ts | 12 +- ...cise-instructor-repo-download.component.ts | 12 +- ...e-instructor-submission-state.component.ts | 14 +- ...structor-trigger-build-button.component.ts | 20 +- ...g-exercise-re-evaluate-button.component.ts | 12 +- ...xercise-student-repo-download.component.ts | 12 +- ...-student-trigger-build-button.component.ts | 11 +- ...g-exercise-trigger-all-button.component.ts | 22 +- ...exercise-trigger-build-button.component.ts | 16 +- ...ild-plan-checkout-directories.component.ts | 1 - ...ise-edit-checkout-directories.component.ts | 9 +- ...sitory-and-build-plan-details.component.ts | 9 +- .../actions/code-editor-actions.component.ts | 26 +- ...-editor-confirm-refresh-modal.component.ts | 16 +- ...editor-resolve-conflict-modal.component.ts | 10 +- .../code-editor-build-output.component.ts | 18 +- .../shared/code-editor/code-editor.module.ts | 5 +- .../code-editor-container.component.ts | 22 +- ...ode-editor-file-browser-badge.component.ts | 13 +- ...itor-file-browser-create-node.component.ts | 2 + .../code-editor-file-browser-delete.ts | 15 +- ...code-editor-file-browser-file.component.ts | 5 + ...de-editor-file-browser-folder.component.ts | 4 + ...code-editor-file-browser-node.component.ts | 4 +- .../code-editor-file-browser.component.ts | 34 +- .../header/code-editor-header.component.ts | 4 +- .../code-editor-instructions.component.ts | 6 +- .../layout/code-editor-grid.component.ts | 8 +- ...e-editor-repository-is-locked.component.ts | 5 + .../monaco/code-editor-monaco.component.ts | 19 +- .../code-editor-conflict-state.service.ts | 12 +- ...ditor-domain-dependent-endpoint.service.ts | 12 +- .../code-editor-domain-dependent.service.ts | 6 +- .../service/code-editor-domain.service.ts | 2 - .../service/code-editor-repository.service.ts | 43 +- .../service/code-editor-submission.service.ts | 14 +- .../status/code-editor-status.component.ts | 5 + .../treeview-item/treeview-item.component.ts | 3 +- .../components/treeview/treeview.component.ts | 4 +- .../code-editor/treeview/treeview.module.ts | 3 +- .../commits-info-group.component.ts | 3 + .../commits-info-row.component.ts | 8 + .../commits-info/commits-info.component.ts | 14 +- ...rogramming-exercise-plant-uml.extension.ts | 10 +- ...amming-exercise-instruction.component.html | 2 +- ...gramming-exercise-instruction.component.ts | 47 +- ...ing-exercise-instructions-render.module.ts | 12 +- ...rcise-instruction-step-wizard.component.ts | 15 +- ...rcise-instruction-task-status.component.ts | 14 +- ...rogramming-exercise-lifecycle.component.ts | 34 +- .../programming-exercise-lifecycle.module.ts | 10 +- ...ise-test-schedule-date-picker.component.ts | 8 +- .../shared/programming-exercise.module.ts | 5 +- .../shared/service/aeolus.service.ts | 6 +- .../shared/service/build-log.service.ts | 6 +- .../shared/service/file-type.service.ts | 2 - .../programming-language-feature.service.ts | 6 +- .../shared/service/theia.service.ts | 6 +- .../apollon-diagram-create-form.component.ts | 16 +- .../apollon-diagram-detail.component.ts | 30 +- ...apollon-diagram-import-dialog.component.ts | 9 +- .../apollon-diagram-list.component.ts | 26 +- .../apollon-diagram.module.ts | 3 +- .../apollon-diagram.service.ts | 10 +- .../drag-and-drop-question-edit.component.ts | 56 +- .../match-percentage-info-modal.component.ts | 8 +- ...multiple-choice-question-edit.component.ts | 30 +- ...mport-invalid-questions-modal.component.ts | 11 +- .../quiz-exercise-create-buttons.component.ts | 15 +- .../manage/quiz-exercise-detail.component.ts | 28 +- .../manage/quiz-exercise-export.component.ts | 21 +- ...iz-exercise-lifecycle-buttons.component.ts | 14 +- .../quiz-exercise-manage-buttons.component.ts | 25 +- .../manage/quiz-exercise-paging.service.ts | 7 +- .../manage/quiz-exercise-popup.service.ts | 12 +- .../manage/quiz-exercise-update.component.ts | 67 +- .../quiz-exercise-validation.directive.ts | 10 +- .../quiz/manage/quiz-exercise.component.ts | 24 + .../quiz/manage/quiz-exercise.service.ts | 13 +- .../quiz/manage/quiz-management.module.ts | 11 +- .../quiz/manage/quiz-management.route.ts | 23 +- ...iz-pool-mapping-question-list.component.ts | 3 +- .../manage/quiz-pool-mapping.component.ts | 15 +- .../quiz/manage/quiz-pool.component.ts | 26 +- .../quiz/manage/quiz-pool.service.ts | 4 +- ...z-question-list-edit-existing.component.ts | 26 +- .../quiz-question-list-edit.component.ts | 19 +- .../quiz-scoring-info-modal.component.ts | 8 +- ...aluate-drag-and-drop-question.component.ts | 3 +- ...uate-multiple-choice-question.component.ts | 9 +- .../quiz-re-evaluate-warning.component.ts | 19 +- .../re-evaluate/quiz-re-evaluate.component.ts | 46 +- .../re-evaluate/quiz-re-evaluate.service.ts | 6 +- ...valuate-short-answer-question.component.ts | 4 +- .../short-answer-question-edit.component.ts | 48 +- ...g-and-drop-question-statistic.component.ts | 45 +- ...ple-choice-question-statistic.component.ts | 35 +- .../question-statistic.component.ts | 40 +- .../quiz-point-statistic.component.ts | 53 +- .../statistics/quiz-statistic.module.ts | 8 +- .../manage/statistics/quiz-statistic.route.ts | 16 +- .../quiz-statistic.component.ts | 41 +- .../quiz-statistics-footer.component.ts | 31 +- .../quiz/manage/statistics/quiz-statistics.ts | 10 +- ...ort-answer-question-statistic.component.ts | 40 +- .../quiz-participation.component.ts | 50 +- .../participate/quiz-participation.module.ts | 7 +- .../participate/quiz-participation.route.ts | 5 +- .../participate/quiz-participation.service.ts | 8 +- .../drag-and-drop-question-util.service.ts | 2 - .../shared/fit-text/fit-text.directive.ts | 20 +- .../quiz/shared/fit-text/fit-text.module.ts | 8 - .../artemis-quiz-question-types.module.ts | 8 +- .../drag-and-drop-question.component.ts | 43 +- .../drag-item.component.ts | 8 +- .../multiple-choice-question.component.ts | 13 +- ...ltiple-choice-visual-question.component.ts | 9 +- ...iz-scoring-info-student-modal.component.ts | 14 +- .../short-answer-question.component.ts | 17 +- .../shared/quiz-statistic-util.service.ts | 4 +- .../short-answer-question-util.service.ts | 2 - .../assessment-progress-label.component.ts | 3 + .../assessment-progress-label.module.ts | 3 +- .../course-exercise.service.ts | 14 +- ...ercise-assessment-dashboard.component.html | 231 +- ...exercise-assessment-dashboard.component.ts | 110 +- .../exercise-assessment-dashboard.module.ts | 5 +- .../language-table-cell.component.ts | 2 + ...cond-correction-enable-button.component.ts | 3 + .../tutor/tutor-participation.service.ts | 10 +- .../difficulty-picker.component.ts | 3 + .../difficulty-picker.module.ts | 3 +- .../exam-exercise-row-buttons.component.ts | 27 +- .../example-solution.component.ts | 20 +- ...xample-submission-import-paging.service.ts | 6 +- .../example-submission-import.component.ts | 43 +- .../example-submission.service.ts | 8 +- .../example-submissions.component.ts | 27 +- .../example-submissions.module.ts | 11 +- ...xercise-detail-common-actions.component.ts | 26 +- ...g-exercise-detail-common-actions.module.ts | 10 +- .../difficulty-badge.component.ts | 10 +- .../exercise-headers-information.component.ts | 11 +- .../exercise-headers.module.ts | 10 +- ...er-exercise-page-with-details.component.ts | 26 +- .../header-participation-page.component.ts | 21 +- .../included-in-score-badge.component.ts | 11 +- .../exercise-info/exercise-info.component.ts | 5 + .../exercise-info/exercise-info.module.ts | 3 +- ...exercise-scores-export-button.component.ts | 15 +- .../exercise-scores.component.ts | 69 +- .../exercise-scores/exercise-scores.module.ts | 5 +- .../manage-assessment-buttons.component.ts | 23 +- .../exercise-title-channel-name.component.ts | 14 +- .../exercise-title-channel-name.module.ts | 3 +- .../exercise-update-notification.component.ts | 5 +- .../exercise-update-notification.module.ts | 3 +- .../exercise-update-warning.component.ts | 10 +- .../exercise-update-warning.module.ts | 3 +- .../exercise-update-warning.service.ts | 6 +- .../shared/exercise/exercise-cache.service.ts | 4 +- .../shared/exercise/exercise.component.ts | 6 +- .../shared/exercise/exercise.module.ts | 11 +- .../shared/exercise/exercise.service.ts | 14 +- .../external-submission-button.component.ts | 8 +- .../external-submission-dialog.component.ts | 20 +- .../external-submission.service.ts | 9 +- ...e-feedback-suggestion-options.component.ts | 17 +- ...cise-feedback-suggestion-options.module.ts | 3 +- .../collapse/feedback-collapse.component.ts | 4 + .../feedback-suggestion-badge.component.ts | 10 +- ...s-pending-confirmation-dialog.component.ts | 11 +- .../shared/feedback/feedback.component.ts | 53 +- .../shared/feedback/feedback.module.ts | 7 +- .../feedback/item/feedback-item-service.ts | 4 +- .../item/programming-feedback-item.service.ts | 4 +- .../feedback/long-feedback-text.service.ts | 10 +- .../feedback/node/feedback-node.component.ts | 7 + .../standalone-feedback.component.ts | 14 +- .../feedback/text/feedback-text.component.ts | 8 +- .../import/exercise-import-tabs.component.ts | 16 + .../exercise-import-wrapper.component.ts | 11 +- .../import/exercise-import.component.ts | 27 +- .../exercise-import-from-file.component.ts | 13 +- ...luded-in-overall-score-picker.component.ts | 4 +- ...included-in-overall-score-picker.module.ts | 3 +- .../exercise-create-buttons.component.ts | 15 +- .../manage/exercise-create-buttons.module.ts | 3 +- .../mode-picker/mode-picker.component.ts | 3 + .../shared/mode-picker/mode-picker.module.ts | 3 +- .../participation-submission.component.ts | 40 +- .../participation-submission.module.ts | 10 +- .../participation.component.html | 2 +- .../participation/participation.component.ts | 57 +- .../participation/participation.module.ts | 5 +- .../participation/participation.service.ts | 12 +- .../exercise-update-plagiarism.component.ts | 7 +- .../exercise-update-plagiarism.module.ts | 3 +- .../plagiarism-details.component.ts | 3 + .../plagiarism-header.component.ts | 13 +- .../plagiarism-inspector.component.ts | 55 +- .../plagiarism-run-details.component.ts | 12 +- .../plagiarism-sidebar.component.ts | 5 + .../modeling-submission-viewer.component.ts | 9 +- .../plagiarism-split-view.component.ts | 12 +- .../split-pane-header.component.ts | 6 + .../text-submission-viewer.component.ts | 17 +- .../shared/plagiarism/plagiarism.module.ts | 12 +- .../presentation-score.component.ts | 13 +- .../presentation-score.module.ts | 3 +- .../rating-list/rating-list.component.ts | 20 +- .../shared/rating/rating.component.ts | 13 +- .../exercises/shared/rating/rating.module.ts | 3 +- .../exercises/shared/rating/rating.service.ts | 6 +- .../star-rating/star-rating.component.ts | 1 - .../shared/result/repository.service.ts | 10 +- .../result-progress-bar.component.ts | 1 - .../shared/result/result.component.ts | 44 +- .../exercises/shared/result/result.module.ts | 11 +- .../exercises/shared/result/result.service.ts | 15 +- .../result/updating-result.component.ts | 12 +- .../statistics/doughnut-chart.component.ts | 16 +- .../exercise-detail-statistics.component.ts | 2 + .../exercise-statistics.component.ts | 18 +- .../grading-instructions-details.component.ts | 21 +- .../structured-grading-criterion.module.ts | 3 +- .../submission-export-button.component.ts | 8 +- .../submission-export-dialog.component.ts | 20 +- .../submission-export.service.ts | 6 +- .../submission-policy-update.component.ts | 6 +- .../submission-policy-update.module.ts | 3 +- .../submission-version.service.ts | 6 +- .../shared/submission/submission.service.ts | 12 +- .../team-config-form-group.component.ts | 8 +- .../team-config-form-group.module.ts | 3 +- .../team-submission-sync.component.ts | 12 +- .../team-submission-sync.module.ts | 8 - .../team-exercise-search.component.ts | 13 +- .../team-owner-search.component.ts | 9 +- .../team-participate-info-box.component.ts | 3 + .../team-participate.module.ts | 3 +- .../team-students-list.component.ts | 2 + .../team-students-online-list.component.ts | 14 +- .../team-participation-table.component.ts | 35 +- .../team-student-search.component.ts | 9 +- .../team-delete-button.component.ts | 13 +- .../team-update-button.component.ts | 8 +- .../team-update-dialog.component.ts | 22 +- .../exercises/shared/team/team.component.ts | 43 +- .../app/exercises/shared/team/team.module.ts | 2 - .../app/exercises/shared/team/team.route.ts | 7 +- .../app/exercises/shared/team/team.service.ts | 12 +- .../teams-export-button.component.ts | 12 +- .../teams-import-button.component.ts | 8 +- .../teams-import-dialog.component.ts | 35 +- .../teams-import-from-file-form.component.ts | 14 +- .../exercises/shared/team/teams.component.ts | 47 +- .../unreferenced-feedback.component.ts | 13 +- .../manual-textblock-selection.component.ts | 12 +- .../text-assessment-area.component.ts | 4 + .../text-submission-assessment.component.ts | 21 +- .../text-submission-assessment.module.ts | 5 +- .../text-submission-assessment.route.ts | 8 +- .../textblock-assessment-card.component.ts | 1 + .../textblock-feedback-dropdown.component.ts | 2 + .../textblock-feedback-editor.component.ts | 25 +- .../example-text-submission.component.ts | 24 + .../example-text-submission.module.ts | 3 +- .../example-text-submission.route.ts | 4 +- .../resizable-instructions.component.ts | 7 +- .../manage/text-exercise-management.module.ts | 4 +- .../text-exercise-detail.component.ts | 6 + .../text-exercise-row-buttons.component.ts | 5 + .../text-exercise-update.component.ts | 42 +- .../text-exercise/text-exercise.component.ts | 23 +- .../text-exercise/text-exercise.module.ts | 9 +- .../text-exercise/text-exercise.route.ts | 20 +- .../tutor-effort-statistics.component.ts | 6 + .../tutor-effort-statistics.module.ts | 3 +- .../tutor-effort-statistics.route.ts | 3 +- .../text/participate/string-count.service.ts | 2 - .../text/participate/text-editor.component.ts | 80 +- .../text/participate/text-editor.route.ts | 6 +- .../participate/text-participation.module.ts | 6 +- .../text-result/text-result.component.ts | 5 + .../text/shared/text-select.directive.ts | 4 +- .../text/shared/text-shared.module.ts | 12 - .../webapp/app/faq/faq-update.component.ts | 3 +- src/main/webapp/app/faq/faq.component.ts | 6 +- .../feature-overview.component.ts | 14 +- .../feature-overview.module.ts | 3 +- .../feature-overview.route.ts | 5 +- ...orms.module.ts => artemis-forms.module.ts} | 12 +- .../form-footer/form-footer.component.ts | 22 +- .../form-status-bar.component.ts | 3 + .../base-grading-system.component.ts | 4 +- .../grading-system/bonus/bonus.component.ts | 1 - .../detailed-grading-system.component.ts | 1 - .../grading-key-overview.component.ts | 1 - .../grading-key-table.component.ts | 1 - .../grading-system-info-modal.component.ts | 1 - .../grading-system-presentations.component.ts | 3 +- .../grading-system.component.ts | 1 - .../grading-system/grading-system.route.ts | 6 +- .../interval-grading-system.component.ts | 1 - .../app/guided-tour/guided-tour.component.ts | 18 +- .../app/guided-tour/guided-tour.module.ts | 3 +- .../app/guided-tour/guided-tour.service.ts | 22 +- src/main/webapp/app/home/home.component.ts | 1 - .../home/saml2-login/saml2-login.component.ts | 2 - .../webapp/{content => app}/icons/icons.ts | 0 .../iris/about-iris/about-iris.component.ts | 1 - .../chat-status-bar.component.ts | 5 +- .../iris-base-chatbot.component.ts | 49 +- .../course-chatbot.component.ts | 8 +- .../exercise-chatbot-button.component.ts | 20 +- .../widget/chatbot-widget.component.ts | 14 +- .../webapp/app/iris/iris-chat-http.service.ts | 6 +- src/main/webapp/app/iris/iris-chat.service.ts | 24 +- .../webapp/app/iris/iris-guard.service.ts | 8 +- .../iris-logo-button.component.ts | 8 + .../app/iris/iris-logo/iris-logo.component.ts | 1 - .../webapp/app/iris/iris-status.service.ts | 10 +- .../webapp/app/iris/iris-websocket.service.ts | 10 +- src/main/webapp/app/iris/iris.module.ts | 25 +- ...s-course-settings-update-routing.module.ts | 4 +- .../iris-course-settings-update.component.ts | 14 +- ...exercise-settings-update-routing.module.ts | 4 +- ...iris-exercise-settings-update.component.ts | 8 +- ...s-global-settings-update-routing.module.ts | 4 +- .../iris-global-settings-update.component.ts | 2 + ...is-common-sub-settings-update.component.ts | 36 +- .../iris-settings-update.component.ts | 18 +- .../settings/shared/iris-enabled.component.ts | 9 +- .../settings/shared/iris-settings.service.ts | 6 +- .../webapp/app/lecture/attachment.service.ts | 6 +- .../close-edit-lecture-modal.component.ts | 1 - .../lecture/lecture-attachments.component.ts | 59 +- .../app/lecture/lecture-detail.component.ts | 24 +- .../app/lecture/lecture-import.component.ts | 20 +- .../app/lecture/lecture-paging.service.ts | 6 +- .../lecture-period.component.ts | 4 + .../lecture-title-channel-name.component.ts | 2 + .../attachment-unit-form.component.ts | 10 +- .../attachment-units.component.ts | 24 +- .../attachmentUnit.service.ts | 10 +- .../create-attachment-unit.component.ts | 17 +- .../create-exercise-unit.component.ts | 24 +- .../create-online-unit.component.ts | 18 +- .../create-text-unit.component.ts | 17 +- .../create-video-unit.component.ts | 18 +- .../edit-attachment-unit.component.ts | 21 +- .../edit-online-unit.component.ts | 18 +- .../edit-text-unit.component.ts | 18 +- .../edit-video-unit.component.ts | 18 +- .../exerciseUnit.service.ts | 10 +- .../lecture-unit-layout.component.ts | 3 +- .../lecture-unit-management.component.html | 2 +- .../lecture-unit-management.component.ts | 37 +- .../lecture-unit-management.module.ts | 2 - .../lecture-unit-management.route.ts | 46 +- .../lectureUnit.service.ts | 12 +- .../online-resource-dto.model.ts | 2 - .../online-unit-form.component.ts | 9 +- .../onlineUnit.service.ts | 10 +- .../text-unit-form.component.ts | 28 +- .../textUnit.service.ts | 10 +- .../unit-creation-card.component.ts | 6 + .../video-unit-form.component.ts | 7 +- .../videoUnit.service.ts | 10 +- .../lecture-units/lecture-units.component.ts | 31 +- .../app/lecture/lecture-update.component.ts | 21 + .../webapp/app/lecture/lecture.component.ts | 50 +- src/main/webapp/app/lecture/lecture.module.ts | 6 +- src/main/webapp/app/lecture/lecture.route.ts | 29 +- .../webapp/app/lecture/lecture.service.ts | 14 +- .../pdf-preview-enlarged-canvas.component.ts | 12 +- .../pdf-preview-thumbnail-grid.component.ts | 10 +- .../pdf-preview/pdf-preview.component.ts | 1 - .../build-agent-details.component.ts | 1 - .../build-agent-summary.component.ts | 1 - .../build-queue/build-queue.component.ts | 65 +- .../build-queue/build-queue.service.ts | 5 +- .../app/localci/localci-guard.service.ts | 11 +- .../commit-details-view.component.ts | 16 +- .../commit-history.component.ts | 14 +- .../app/localvc/localvc-guard.service.ts | 11 +- .../repository-view.component.ts | 42 +- ...cs-repository-access-log-view.component.ts | 18 +- .../app/lti/lti-course-card.component.ts | 1 - src/main/webapp/app/lti/lti.module.ts | 5 +- src/main/webapp/app/lti/lti.route.ts | 16 +- .../app/lti/lti13-deep-linking.component.ts | 13 +- .../lti13-dynamic-registration.component.ts | 1 - .../lti/lti13-exercise-launch.component.ts | 1 - .../app/lti/lti13-select-content.component.ts | 1 - .../app/lti/lti13-select-course.component.ts | 1 - .../webapp/app/lti/online-course-dto.model.ts | 2 - ...orion-assessment-instructions.component.ts | 2 + .../assessment/orion-assessment.service.ts | 32 +- ...exercise-assessment-dashboard.component.ts | 25 +- .../orion-tutor-assessment.component.ts | 17 +- ...or-and-editor-orion-container.component.ts | 59 +- ...n-course-management-exercises.component.ts | 3 + .../orion-programming-exercise.component.html | 26 +- .../orion-programming-exercise.component.ts | 24 +- ...orion-course-exercise-details.component.ts | 3 + ...ise-details-student-actions.component.html | 60 +- ...rcise-details-student-actions.component.ts | 31 +- .../course-archive.component.ts | 1 - .../course-card-header.component.ts | 1 - .../app/overview/course-card.component.ts | 15 +- .../course-competencies-details.component.ts | 55 +- .../course-competencies-details.module.ts | 10 +- .../course-competencies.component.ts | 21 +- .../course-competencies.module.ts | 9 +- ...conversations-code-of-conduct.component.ts | 13 +- .../course-conversations.component.ts | 87 +- .../course-conversations.module.ts | 11 +- .../course-wide-search.component.ts | 22 +- .../dialogs/abstract-dialog.component.ts | 5 +- .../channel-form/channel-form.component.ts | 14 +- .../channels-create-dialog.component.ts | 8 +- .../channel-item/channel-item.component.ts | 4 + .../channels-overview-dialog.component.ts | 29 +- .../conversation-add-users-form.component.ts | 14 +- ...conversation-add-users-dialog.component.ts | 26 +- .../conversation-detail-dialog.component.ts | 19 +- .../conversation-info.component.ts | 4 + .../conversation-member-row.component.ts | 19 +- .../conversation-members.component.ts | 9 +- .../conversation-settings.component.ts | 4 + .../generic-confirmation-dialog.component.ts | 23 +- ...c-update-text-property-dialog.component.ts | 37 +- .../group-chat-create-dialog.component.ts | 21 +- ...one-to-one-chat-create-dialog.component.ts | 12 +- .../conversation-header.component.ts | 19 +- .../conversation-messages.component.ts | 56 +- .../conversation-thread-sidebar.component.ts | 7 + .../channel-icon/channel-icon.component.ts | 2 + .../posting-summary.component.ts | 9 + .../saved-posts/saved-posts.component.ts | 6 +- .../course-dashboard.component.ts | 38 +- .../course-dashboard.module.ts | 10 +- .../course-dashboard.service.ts | 6 +- .../course-exercise-lateness.component.ts | 14 +- .../course-exercise-performance.component.ts | 14 +- .../course-exams/course-exams.component.ts | 24 +- .../course-exams/course-exams.module.ts | 3 +- .../course-exams/course-exams.route.ts | 3 +- .../course-exercise-row.component.ts | 42 +- .../course-exercise-row.module.ts | 2 +- .../course-exercises.component.ts | 26 +- .../course-faq-accordion-component.ts | 5 +- .../course-faq/course-faq.component.ts | 12 +- .../attachment-unit.component.ts | 1 - .../course-lecture-details.component.ts | 53 +- .../course-lecture-details.module.ts | 7 +- .../course-lecture-row.component.ts | 11 +- .../course-lectures.component.ts | 20 +- .../exercise-unit/exercise-unit.component.ts | 2 + .../lecture-unit/lecture-unit.component.ts | 1 - .../lecture-unit/lecture-unit.directive.ts | 13 +- .../course-lectures/lecture-units.module.ts | 3 +- .../online-unit/online-unit.component.ts | 1 - .../text-unit/text-unit.component.ts | 1 - .../video-unit/video-unit.component.ts | 1 - .../app/overview/course-overview.component.ts | 76 +- .../app/overview/course-overview.service.ts | 10 +- .../course-prerequisites-button.component.ts | 8 +- .../course-prerequisites-button.module.ts | 3 +- .../course-registration-button.component.ts | 17 +- .../course-registration-button.module.ts | 3 +- .../course-registration-detail.component.ts | 18 +- .../course-registration-detail.module.ts | 16 +- .../course-prerequisites-modal.component.ts | 16 +- .../course-prerequisites-modal.module.ts | 3 +- .../course-registration.component.ts | 34 +- .../course-registration.module.ts | 5 +- .../app/overview/course-sidebar.service.ts | 2 - .../course-statistics.component.ts | 46 +- .../course-statistics.module.ts | 11 +- .../course-tutorial-group-card.component.ts | 5 + .../course-tutorial-groups.component.ts | 32 +- .../course-tutorial-groups.module.ts | 4 +- .../course-unenrollment-modal.component.ts | 21 +- .../app/overview/courses-routing.module.ts | 34 +- .../webapp/app/overview/courses.component.ts | 39 +- .../webapp/app/overview/courses.module.ts | 10 +- .../discussion-section.component.ts | 24 +- .../course-exercise-details.component.html | 14 +- .../course-exercise-details.component.ts | 104 +- .../course-exercise-details.module.ts | 15 +- .../exercise-buttons.module.ts | 13 +- ...ise-details-student-actions.component.html | 67 +- ...rcise-details-student-actions.component.ts | 66 +- .../lti-initializer-modal.component.ts | 6 +- .../problem-statement.component.ts | 16 +- .../request-feedback-button.component.ts | 1 - .../app/overview/header-course.component.ts | 13 +- .../participation-websocket.service.ts | 10 +- .../result-history.component.ts | 6 +- .../submission-result-status.component.ts | 4 + .../submission-result-status.module.ts | 11 +- .../course-tutorial-group-detail.component.ts | 20 +- .../course-tutorial-group-details.module.ts | 9 +- .../exercise-scores-chart.module.ts | 3 +- .../exercise-scores-chart.service.ts | 5 +- .../exercise-scores-chart.component.ts | 27 +- .../additional-feedback.component.ts | 24 +- .../shared/alert/alert-overlay.component.ts | 11 +- .../auth/has-any-authority.directive.ts | 16 +- .../app/shared/breakpoints/layout.service.ts | 10 +- .../category-selector.component.ts | 28 +- .../category-selector.module.ts | 13 +- .../app/shared/chart/artemis-charts.module.ts | 13 +- .../circular-progress-bar.component.ts | 2 - .../color-selector.component.ts | 12 +- .../color-selector/color-selector.module.ts | 3 +- .../competency-selection.component.ts | 24 +- .../app/shared/components/button.component.ts | 7 + .../checklist-check.component.ts | 2 + .../code-button/code-button.component.ts | 49 +- .../confirm-autofocus-button.component.ts | 8 +- .../confirm-autofocus-modal.component.ts | 10 +- .../copy-icon-button.component.ts | 5 + .../course-exam-archive-button.component.ts | 25 +- .../documentation-button.component.ts | 9 +- .../documentation-link.component.ts | 1 - .../exercise-action-button.component.ts | 5 +- .../shared/components/help-icon.component.ts | 4 + .../components/not-released-tag.component.ts | 7 +- .../open-code-editor-button.component.ts | 15 +- .../reset-repo-button.component.ts | 20 +- .../components/shared-component.module.ts | 7 +- .../start-practice-mode-button.component.ts | 27 +- .../confirm-entity-name.component.ts | 22 +- .../confirm-icon/confirm-icon.component.ts | 4 + .../confirm-icon/confirm-icon.module.ts | 3 +- .../connection-status.component.ts | 10 +- .../connection-warning.component.ts | 17 +- .../consistency-check.component.ts | 16 +- .../consistency-check.service.ts | 6 +- .../course-group/course-group.component.ts | 44 +- .../course-group/course-group.module.ts | 3 +- .../course-users-selector.component.ts | 15 +- .../course-users-selector.module.ts | 3 +- .../tutor-leaderboard.component.ts | 18 +- .../tutor-leaderboard.model.ts | 2 - .../tutor-leaderboard.module.ts | 3 +- .../progress-bar/progress-bar.component.ts | 13 +- .../tutor-participation-graph.component.ts | 12 +- .../tutor-participation-graph.module.ts | 3 +- .../shared/data-table/data-table.component.ts | 41 +- .../shared/data-table/data-table.module.ts | 3 +- .../date-time-picker.component.ts | 20 +- .../date-time-picker.module.ts | 12 +- .../delete-dialog/delete-button.directive.ts | 14 +- .../delete-dialog/delete-dialog.component.ts | 18 +- .../delete-dialog/delete-dialog.service.ts | 10 +- ...etail-overview-navigation-bar.component.ts | 2 + .../difficulty-level.component.ts | 7 +- ...ustom-exercise-category-badge.component.ts | 3 +- .../exercise-categories.component.ts | 10 +- .../exercise-categories.module.ts | 3 +- .../exercise-filter-modal.component.ts | 12 +- .../shared/export/export-button.component.ts | 8 +- .../shared/export/export-modal.component.ts | 31 +- .../webapp/app/shared/export/export.module.ts | 3 +- .../extension-point.directive.ts | 10 +- .../feature-toggle-hide.directive.ts | 10 +- .../feature-toggle-link.directive.ts | 10 +- .../feature-toggle.directive.ts | 10 +- .../feature-toggle/feature-toggle.module.ts | 10 - .../feature-toggle/feature-toggle.service.ts | 10 +- .../browser-fingerprint.service.ts | 6 +- .../app/shared/fireworks/fireworks.module.ts | 10 - .../title-channel-name.component.ts | 6 +- .../title-channel-name.module.ts | 3 +- .../shared/fullscreen/fullscreen.component.ts | 10 +- .../shared/fullscreen/fullscreen.module.ts | 3 +- ...grading-instruction-link-icon.component.ts | 11 +- .../grading-instruction-link-icon.module.ts | 3 +- .../app/shared/guard/pending-changes.guard.ts | 4 +- .../webapp/app/shared/http/file.service.ts | 5 +- .../app/shared/http/secure-link.directive.ts | 10 +- .../shared/icon-card/icon-card.component.ts | 1 - .../component/image-cropper.component.ts | 16 +- .../image-cropper/image-cropper.module.ts | 10 - .../shared/image/cacheable-image.service.ts | 10 +- .../shared/image/secured-image.component.ts | 15 +- .../import-list/import-table.component.ts | 8 +- .../app/shared/import/import.component.ts | 21 +- .../shared/info-panel/info-panel.component.ts | 2 - .../shared/info-panel/info-panel.module.ts | 3 +- .../information-box.component.ts | 1 - .../shared/language/translate.directive.ts | 13 +- .../shared/layouts/error/error.component.ts | 8 +- .../app/shared/layouts/error/error.route.ts | 9 +- .../shared/layouts/footer/footer.component.ts | 10 +- .../layouts/navbar/active-menu.directive.ts | 16 +- .../layouts/navbar/entity-title.service.ts | 6 +- .../layouts/navbar/navbar.component.html | 2 +- .../shared/layouts/navbar/navbar.component.ts | 79 +- .../layouts/profiles/page-ribbon.component.ts | 8 +- .../layouts/profiles/profile.service.ts | 12 +- .../link-preview-container.component.ts | 2 + .../link-preview/link-preview.component.ts | 12 +- .../services/link-preview.service.ts | 6 +- .../loading-indicator-container.component.ts | 1 - .../markdown-editor/markdown-editor.module.ts | 3 +- .../markdown-editor-monaco.component.ts | 46 +- .../webapp/app/shared/markdown.service.ts | 4 +- .../app/shared/metis/answer-post.service.ts | 6 +- .../answer-post/answer-post.component.ts | 60 +- .../metis/course-discussion.directive.ts | 6 +- .../metis/emoji/emoji-picker.component.ts | 1 - .../app/shared/metis/emoji/emoji.component.ts | 1 - .../message-inline-input.component.ts | 22 +- .../message-reply-inline-input.component.ts | 25 +- .../metis/metis-conversation.service.ts | 28 +- .../webapp/app/shared/metis/metis.module.ts | 2 - .../webapp/app/shared/metis/metis.service.ts | 25 +- .../webapp/app/shared/metis/post.service.ts | 9 +- .../app/shared/metis/post/post.component.ts | 69 +- .../posting-button.component.ts | 4 +- .../enlarge-slide-image.component.ts | 8 +- .../posting-content-part.components.ts | 17 +- .../posting-content.components.ts | 14 +- ...answer-post-create-edit-modal.component.ts | 18 +- .../post-create-edit-modal.component.html | 30 - .../post-create-edit-modal.component.ts | 32 +- .../post-tag-selector.component.ts | 38 +- .../posting-create-edit-modal.directive.ts | 13 +- .../metis/posting-create-edit.directive.ts | 15 +- .../posting-footer.component.ts | 5 +- .../posting-header.component.ts | 31 +- .../posting-markdown-editor.component.ts | 23 +- .../answer-post-reactions-bar.component.ts | 45 +- .../post-reactions-bar.component.ts | 54 +- .../posting-reactions-bar.directive.ts | 14 +- .../posting-thread.component.ts | 2 + .../app/shared/metis/posting.service.ts | 2 +- .../app/shared/metis/reaction.service.ts | 6 +- .../monaco-diff-editor.component.ts | 3 +- .../monaco-editor-option.helper.ts | 2 +- .../monaco-editor/monaco-editor.component.ts | 17 +- .../monaco-editor/monaco-editor.service.ts | 8 +- .../webapp/app/shared/no-data-component.ts | 1 - .../loading-notification.component.ts | 6 +- .../loading-notification.interceptor.ts | 6 +- .../notification-popup.component.ts | 19 +- .../notification-sidebar.component.ts | 25 +- .../notification/notification.service.ts | 22 +- .../admin-system-notification.service.ts | 12 +- .../system-notification.component.ts | 17 +- .../system-notification.service.ts | 10 +- .../organization-selector.component.ts | 16 +- .../modal-confirm-autofocus.component.ts | 8 +- .../orion/orion-build-and-test.service.ts | 14 +- .../orion-button/orion-button.component.ts | 5 +- .../shared/orion/orion-connector.service.ts | 24 +- .../shared/orion/orion-filter.directive.ts | 10 +- .../webapp/app/shared/orion/orion.module.ts | 14 +- .../orion-outdated.component.ts | 13 +- .../orion-version-validator.service.ts | 10 +- .../shared/pagination/item-count.component.ts | 2 + ...rticipant-scores-distribution.component.ts | 16 +- .../participant-scores.module.ts | 3 +- .../participant-scores.service.ts | 6 +- .../shared/pipes/artemis-date-range.pipe.ts | 5 +- .../app/shared/pipes/artemis-date.pipe.ts | 9 +- .../app/shared/pipes/artemis-time-ago.pipe.ts | 12 +- .../shared/pipes/artemis-translate.pipe.ts | 2 +- .../webapp/app/shared/pipes/duration.pipe.ts | 4 +- .../pipes/exercise-course-title.pipe.ts | 4 +- .../app/shared/pipes/exercise-type.pipe.ts | 4 +- .../app/shared/pipes/feedback-content.pipe.ts | 1 - .../shared/pipes/grade-step-bounds.pipe.ts | 1 - .../shared/pipes/html-for-markdown.pipe.ts | 1 - .../pipes/html-for-posting-markdown.pipe.ts | 1 - .../webapp/app/shared/pipes/quote.pipe.ts | 1 - .../pipes/reacting-users-on-posting.pipe.ts | 8 +- .../webapp/app/shared/pipes/safe-html.pipe.ts | 6 +- .../shared/pipes/safe-resource-url.pipe.ts | 4 +- .../webapp/app/shared/pipes/safe-url.pipe.ts | 4 +- .../app/shared/pipes/search-filter.pipe.ts | 4 +- .../app/shared/pipes/shared-pipes.module.ts | 2 +- .../profile-picture.component.ts | 3 +- .../range-slider/range-slider.component.ts | 7 +- .../resizeable-container.component.ts | 7 +- .../app/shared/science/science.component.ts | 5 +- .../app/shared/science/science.directive.ts | 9 +- .../app/shared/science/science.service.ts | 14 +- .../score-display/score-display.component.ts | 7 +- .../search-filter/search-filter.component.ts | 6 +- .../webapp/app/shared/server-date.service.ts | 6 +- .../shared/service/legal-document.service.ts | 6 +- .../service/locale-conversion.service.ts | 9 +- .../webapp/app/shared/service/lti.service.ts | 1 - .../webapp/app/shared/service/sort.service.ts | 2 - .../shared/service/static-content.service.ts | 6 +- .../webapp/app/shared/shared-common.module.ts | 22 +- .../webapp/app/shared/shared-libs.module.ts | 13 - src/main/webapp/app/shared/shared.module.ts | 13 +- .../shared/side-panel/side-panel.component.ts | 2 - .../shared/side-panel/side-panel.module.ts | 3 +- .../conversation-options.component.ts | 22 +- .../sidebar-accordion.component.ts | 9 +- .../sidebar-card-item.component.ts | 18 + .../sidebar-card-large.component.ts | 17 +- .../sidebar-card-medium.component.ts | 20 +- .../sidebar-card-small.component.ts | 21 +- .../shared/sidebar/sidebar-card.directive.ts | 7 +- .../app/shared/sidebar/sidebar.component.ts | 47 +- .../app/shared/sidebar/sidebar.module.ts | 2 - .../app/shared/sort/sort-by.directive.ts | 12 +- .../app/shared/sort/sort-icon.component.ts | 1 - .../webapp/app/shared/sort/sort.directive.ts | 4 +- .../knowledge-area-tree.component.ts | 5 +- ...tandardized-competency-detail.component.ts | 3 - ...rdized-competency-filter-page.component.ts | 4 +- ...tandardized-competency-filter.component.ts | 4 +- .../standardized-competency.service.ts | 6 +- ...tatistics-average-score-graph.component.ts | 22 +- .../statistics-graph.component.ts | 15 +- ...tics-score-distribution-graph.component.ts | 10 +- .../statistics-graph/statistics.service.ts | 6 +- .../sticky-popover.directive.ts | 16 +- .../table-editable-checkbox.component.ts | 2 + .../table/table-editable-field.component.ts | 6 +- .../webapp/app/shared/table/table.module.ts | 5 +- .../textarea/textarea-counter.component.ts | 8 +- .../app/shared/textarea/textarea.module.ts | 3 +- .../type-ahead-user-search-field.component.ts | 12 +- .../type-ahead-user-search-field.module.ts | 3 +- .../shared/user-import/user-import.module.ts | 3 +- .../users-import-button.component.ts | 8 +- .../users-import-dialog.component.ts | 24 +- .../account-information.component.ts | 19 +- .../ide-preferences/ide-settings.component.ts | 23 +- .../ide-preferences/ide-settings.service.ts | 6 +- .../notification-settings.component.ts | 26 +- .../notification-settings.service.ts | 6 +- .../science-settings.component.ts | 28 +- .../science-settings.service.ts | 10 +- ...ssh-user-settings-key-details.component.ts | 9 +- ...sh-user-settings-fingerprints.component.ts | 4 + .../ssh-user-settings.component.ts | 20 + .../user-settings-container.component.ts | 1 - .../user-settings/user-settings.directive.ts | 12 +- .../user-settings/user-settings.module.ts | 9 +- .../user-settings/user-settings.route.ts | 34 +- .../user-settings/user-settings.service.ts | 6 +- .../vcs-access-tokens-settings.component.ts | 30 +- .../custom-max-validator.directive.ts | 1 - .../custom-min-validator.directive.ts | 1 - ...tom-not-included-in-validator.directive.ts | 2 - .../custom-pattern-validator.directive.ts | 1 - .../vertical-progress-bar.component.ts | 8 +- .../vertical-progress-bar.module.ts | 11 - .../virtual-scroll.component.ts | 10 +- .../virtual-scroll/virtual-scroll.module.ts | 17 - src/main/webapp/app/utils/navigation.utils.ts | 12 +- src/main/webapp/app/utils/text.utils.ts | 15 + src/main/webapp/index.html | 4 +- .../account-information.component.spec.ts | 3 +- .../password-reset-init.component.spec.ts | 7 +- .../account/register.component.spec.ts | 1 - .../account/settings.component.spec.ts | 1 - ...er-settings-fingerprints.component.spec.ts | 2 - ...ser-settings-key-details.component.spec.ts | 3 +- .../ssh/ssh-user-settings.component.spec.ts | 3 +- ...cs-access-token-settings.component.spec.ts | 16 +- .../component/admin/audits.component.spec.ts | 1 - .../admin/configuration.component.spec.ts | 1 - .../admin/health-modal.component.spec.ts | 4 +- .../component/admin/logs.component.spec.ts | 2 - .../admin/lti-configuration.component.spec.ts | 29 +- ...stem-notification-detail.component.spec.ts | 13 +- ...ication-management-resolve.service.spec.ts | 9 +- ...-notification-management.component.spec.ts | 17 +- ...ming-exams-and-exercises.component.spec.ts | 3 - .../user-management-detail.component.spec.ts | 1 - .../user-management-resolve.service.spec.ts | 9 +- .../user-management-update.component.spec.ts | 14 +- .../admin/user-management.component.spec.ts | 26 +- ...llon-diagram-create-form.component.spec.ts | 3 +- .../apollon-diagram-detail.component.spec.ts | 2 +- ...on-diagram-import-dialog.component.spec.ts | 3 +- .../apollon-diagram-list.component.spec.ts | 4 +- ...nt-dashboard-information.component.spec.ts | 9 +- .../assessment-dashboard.component.spec.ts | 35 +- ...ise-assessment-dashboard.component.spec.ts | 6 +- ...correction-enable-button.component.spec.ts | 17 +- ...sessment-complaint-alert.component.spec.ts | 2 - .../assessment-header.component.spec.ts | 12 +- .../assessment-warning.component.spec.ts | 2 - ...-assessment-instructions.component.spec.ts | 11 +- ...ferenced-feedback-detail.component.spec.ts | 30 +- .../code-editor-grid.component.spec.ts | 1 - .../competency-card.component.spec.ts | 16 +- .../competency-form-stub.component.ts | 32 +- .../competency-form.component.spec.ts | 31 +- .../import-all-competencies.component.spec.ts | 4 +- .../competency-popover.component.spec.ts | 2 + ...petencies-relation-graph.component.spec.ts | 38 +- ...petencies-relation-modal.component.spec.ts | 6 +- .../edit-competency.component.spec.ts | 10 +- .../edit-prerequisite.component.spec.ts | 15 +- .../course-description-form-stub.component.ts | 2 +- .../competency-search.component.spec.ts | 4 +- .../import-competencies.component.spec.ts | 4 +- ...port-course-competencies.component.spec.ts | 11 +- .../import-prerequisites.component.spec.ts | 4 +- .../prerequisite-form-stub.component.ts | 32 +- .../prerequisite-form.component.spec.ts | 46 +- .../complaints-form.component.spec.ts | 13 - .../connection-warning.spec.ts | 6 +- ...rse-prerequisites-button.component.spec.ts | 1 - ...urse-prerequisites-modal.component.spec.ts | 11 +- ...urse-registration-button.component.spec.ts | 1 - .../course-group-membership.component.spec.ts | 40 +- .../course-management-card.component.spec.ts | 4 +- ...agement-exercises-search.component.spec.ts | 13 - ...ment-overview-statistics.component.spec.ts | 11 +- .../course-management.component.spec.ts | 27 - .../course/course-overview.component.spec.ts | 10 +- ...ourse-unenrollment-modal.component.spec.ts | 6 +- .../course/course-update.component.spec.ts | 43 +- ...se-detail-doughnut-chart.component.spec.ts | 11 +- ...course-detail-line-chart.component.spec.ts | 11 +- .../course/header-course.component.spec.ts | 19 +- .../image-cropper-modal.component.spec.ts | 4 +- .../detail-overview-list.component.spec.ts | 6 +- ...g-and-drop-question-edit.component.spec.ts | 54 +- .../drag-and-drop-question.component.spec.ts | 4 +- .../drag-item.component.spec.ts | 6 +- ...res-average-scores-graph.component.spec.ts | 8 +- .../exam-scores/exam-scores.component.spec.ts | 55 +- .../exam/exam-update.component.spec.ts | 66 +- ...xam-exercise-row-buttons.component.spec.ts | 30 +- ...nouncement-create-button.component.spec.ts | 5 +- ...nnouncement-create-modal.component.spec.ts | 24 +- .../exercise-group-update.component.spec.ts | 16 +- .../exercise-group.service.spec.ts | 20 +- ...load-exercise-group-cell.component.spec.ts | 8 +- ...ling-exercise-group-cell.component.spec.ts | 9 +- ...ming-exercise-group-cell.component.spec.ts | 11 +- ...quiz-exercise-group-cell.component.spec.ts | 8 +- .../student-exam-detail.component.spec.ts | 39 +- .../student-exam-timeline.component.spec.ts | 4 +- .../student-exam.service.spec.ts | 30 +- .../student-exams.component.spec.ts | 22 +- ...exam-live-events-overlay.component.spec.ts | 13 +- .../participate/exam-bar.component.spec.ts | 12 +- ...m-exercise-overview-page.component.spec.ts | 16 +- .../exam-navigation-bar.component.spec.ts | 21 +- .../exam-navigation-sidebar.component.spec.ts | 23 +- .../exercise-save-button.component.spec.ts | 2 +- ...gramming-exam-submission.component.spec.ts | 28 +- .../text-exam-submission.component.spec.ts | 22 - ...exam-general-information.component.spec.ts | 16 +- .../exam-result-summary.component.spec.ts | 55 +- ...file-upload-exam-summary.component.spec.ts | 9 +- .../modeling-exam-summary.component.spec.ts | 8 +- ...programming-exam-summary.component.spec.ts | 28 +- .../quiz-exam-summary.component.spec.ts | 13 +- .../text-exam-summary.component.spec.ts | 10 +- .../exam-result-overview.component.spec.ts | 11 +- .../events/exam-live-event.component.spec.ts | 20 +- ...tudent-exam-working-time.component.spec.ts | 6 +- .../testexam-working-time.component.spec.ts | 14 +- .../working-time-control.component.spec.ts | 1 - .../create-test-run-modal.component.spec.ts | 16 +- .../exercise-detail.directive.spec.ts | 3 +- .../problem-statement.component.spec.ts | 8 +- ...line-feedback-suggestion.component.spec.ts | 4 +- ...ditor-file-browser-badge.component.spec.ts | 9 +- .../quiz-exercise-detail.component.spec.ts | 2 - ...ercise-lifecycle-buttons.component.spec.ts | 1 - ...-exercise-manage-buttons.component.spec.ts | 1 - ...ol-mapping-question-list.component.spec.ts | 5 - .../quiz-pool-mapping.component.spec.ts | 21 +- .../quiz/manage/quiz-pool.component.spec.ts | 29 +- ...e-drag-and-drop-question.component.spec.ts | 4 +- ...multiple-choice-question.component.spec.ts | 69 +- ...te-short-answer-question.component.spec.ts | 19 +- .../quiz/quiz-participation.component.spec.ts | 345 +- ...oring-info-student-modal.component.spec.ts | 34 +- ...xample-submission-import.component.spec.ts | 2 - .../exercise-scores.component.spec.ts | 32 +- .../exercise-statistics.component.spec.ts | 26 +- ...rcise-title-channel-name.component.spec.ts | 17 +- ...ternal-submission-button.component.spec.ts | 19 +- ...ternal-submission-dialog.component.spec.ts | 3 - ...edback-suggestion-option.component.spec.ts | 1 - .../difficulty-badge.component.spec.ts | 1 - .../included-in-score-badge.component.spec.ts | 1 - .../team-config-form-group.component.spec.ts | 5 +- .../feature-overview.component.spec.ts | 2 - ...e-upload-exercise-update.component.spec.ts | 5 +- .../file-upload-exercise.component.spec.ts | 5 +- .../forms/form-footer.component.spec.ts | 2 +- .../git-diff-file-panel.component.spec.ts | 3 +- .../git-diff-modal.component.spec.ts | 61 +- .../detailed-grading-system.component.spec.ts | 20 +- ...ing-system-presentations.component.spec.ts | 6 +- .../grading-system.component.spec.ts | 10 +- .../interval-grading-system.component.spec.ts | 17 +- .../import/exercise-import.component.spec.ts | 5 +- .../component/import/import.component.spec.ts | 8 +- .../component/iris/iris-chat.service.spec.ts | 1 + ...s-course-settings-update.component.spec.ts | 11 +- .../settings/iris-enabled.component.spec.ts | 3 +- ...exercise-settings-update.component.spec.ts | 11 +- ...s-global-settings-update.component.spec.ts | 11 +- .../iris-settings-update-component.spec.ts | 13 +- .../iris/ui/chat-status-bar.component.spec.ts | 3 +- .../ui/iris-base-chatbot.component.spec.ts | 4 +- ...ning-paths-configuration.component.spec.ts | 4 +- ...ing-path-instructor-page.component.spec.ts | 9 + .../attachment-unit-form.component.spec.ts | 17 +- .../create-attachment-unit.component.spec.ts | 37 +- .../edit-attachment-unit.component.spec.ts | 168 +- .../create-exercise-unit.component.spec.ts | 3 +- .../exercise-unit.component.spec.ts | 28 +- .../create-online-unit.component.spec.ts | 27 +- .../edit-online-unit.component.spec.ts | 35 +- .../create-text-unit.component.spec.ts | 44 +- .../edit-text-unit.component.spec.ts | 73 +- .../text-unit-form.component.spec.ts | 41 +- .../create-video-unit.component.spec.ts | 27 +- .../edit-video-unit.component.spec.ts | 56 +- .../hasLectureUnsavedChanges.guard.spec.ts | 1 - .../lecture/lecture-period.component.spec.ts | 4 +- ...cture-title-channel-name.component.spec.ts | 8 +- .../lecture/lecture-units.component.spec.ts | 51 +- ...export-confirmation-dialog.service.spec.ts | 3 - ...ta-export-request-button.directive.spec.ts | 19 +- .../link-preview-container.component.spec.ts | 1 - .../build-queue/build-queue.component.spec.ts | 14 +- .../localvc/repository-view.component.spec.ts | 7 +- .../exercise-create-buttons.component.spec.ts | 4 +- .../modeling-exercise.component.spec.ts | 4 - ...eling-explanation-editor.component.spec.ts | 6 +- ...modeling-submission-team.component.spec.ts | 1 + ...ple-choice-question-edit.component.spec.ts | 33 +- .../organization-management-resolve.spec.ts | 17 +- .../organization-management.component.spec.ts | 1 - .../organization-selector.component.spec.ts | 8 +- .../modal-confirm-autofocus.component.spec.ts | 7 +- ...tor-and-editor-container.component.spec.ts | 2 +- ...ise-assessment-dashboard.component.spec.ts | 4 +- ...-details-student-actions.component.spec.ts | 2 +- .../orion/orion-outdated.component.spec.ts | 5 +- ...ion-programming-exercise.component.spec.ts | 2 +- .../overview/course-card.component.spec.ts | 19 +- .../course-wide-search.component.spec.ts | 1 - .../channel-form.component.spec.ts | 5 +- .../channels-create-dialog.component.spec.ts | 58 +- .../channel-item.component.spec.ts | 9 +- ...channels-overview-dialog.component.spec.ts | 32 +- ...rsation-add-users-dialog.component.spec.ts | 31 +- ...nversation-detail-dialog.component.spec.ts | 114 +- .../conversation-info.component.spec.ts | 5 +- .../conversation-member-row.component.spec.ts | 11 +- .../conversation-members.component.spec.ts | 42 +- .../conversation-settings.component.spec.ts | 9 +- ...eric-confirmation-dialog.component.spec.ts | 5 +- ...ate-text-property-dialog.component.spec.ts | 5 +- ...group-chat-create-dialog.component.spec.ts | 5 +- ...o-one-chat-create-dialog.component.spec.ts | 5 +- .../conversation-messages.component.spec.ts | 5 +- ...versation-thread-sidebar.component.spec.ts | 4 +- .../posting-summary.component.spec.ts | 8 +- .../course-lecture-row.component.spec.ts | 2 + .../course-statistics.component.spec.ts | 27 +- .../exercise-scores-chart.component.spec.ts | 8 +- .../discussion-section.component.spec.ts | 11 +- .../lti-initializer.component.spec.ts | 4 +- .../participation.component.spec.ts | 34 +- ...deling-submission-viewer.component.spec.ts | 8 +- ...e-instructor-detail-view.component.spec.ts | 14 +- ...case-student-detail-view.component.spec.ts | 8 +- ...sm-cases-instructor-view.component.spec.ts | 32 +- .../plagiarism-inspector.component.spec.ts | 20 +- .../plagiarism-run-details.component.spec.ts | 10 +- .../split-pane-header.component.spec.ts | 19 - .../presentation-score.component.spec.ts | 8 +- ...tor-assessment-container.component.spec.ts | 52 +- ...sessment-inline-feedback.component.spec.ts | 19 +- ...sment-repo-export-button.component.spec.ts | 12 - ...sment-repo-export-dialog.component.spec.ts | 22 +- .../category-issues-chart.component.spec.ts | 4 +- ...cise-build-configuration.component.spec.ts | 4 +- ...ercise-configure-grading.component.spec.ts | 76 +- ...-exercise-create-buttons.component.spec.ts | 2 - ...gramming-exercise-detail.component.spec.ts | 1 - ...g-exercise-edit-selected.component.spec.ts | 1 - ...ise-instruction-analysis.component.spec.ts | 14 +- ...ing-exercise-instruction.component.spec.ts | 44 +- ...tor-trigger-build-button.component.spec.ts | 5 - ...mming-exercise-lifecycle.component.spec.ts | 70 +- ...rcise-re-evaluate-button.component.spec.ts | 5 - ...ng-exercise-reset-dialog.component.spec.ts | 46 +- ...se-student-repo-download.component.spec.ts | 12 +- ...ase-passed-builds-charts.component.spec.ts | 14 +- ...est-schedule-date-picker.component.spec.ts | 42 +- ...rcise-trigger-all-button.component.spec.ts | 5 - ...gramming-exercise-update.component.spec.ts | 200 +- .../programming-exercise.component.spec.ts | 1 - ...egory-distribution-chart.component.spec.ts | 8 +- ...-case-distribution-chart.component.spec.ts | 10 +- ...ramming-exercise-grading.component.spec.ts | 100 +- ...ing-exercise-information.component.spec.ts | 67 +- ...amming-exercise-language.component.spec.ts | 19 +- ...ramming-exercise-problem.component.spec.ts | 53 +- ...ogramming-exercise-theia.component.spec.ts | 3 +- ...exercise-create-buttons.components.spec.ts | 2 - .../quiz-exercise-update.component.spec.ts | 5 +- ...z-exercise-visual-editor.component.spec.ts | 7 +- .../quiz-exercise.component.spec.ts | 2 +- ...quiz-re-evaluate-warning.component.spec.ts | 22 - .../quiz-re-evaluate.component.spec.ts | 23 +- .../drag-and-drop-statistic.component.spec.ts | 1 - ...hoice-question-statistic.component.spec.ts | 1 - .../quiz-point-statistic.component.spec.ts | 1 - .../quiz-statistic.component.spec.ts | 24 +- .../quiz-statistics-footer.component.spec.ts | 25 +- ...nswer-question-statistic.component.spec.ts | 21 +- .../rating/star-rating.component.spec.ts | 4 +- .../ide/ide-settings.component.spec.ts | 8 +- .../shared/alert-overlay.component.spec.ts | 2 - ...ssessment-progress-label.component.spec.ts | 2 - .../circular-progress-bar.component.spec.ts | 8 +- .../shared/color-selector.component.spec.ts | 1 - .../competency-selection.component.spec.ts | 14 +- .../confirm-entity-name.component.spec.ts | 39 +- .../shared/course-group.component.spec.ts | 35 +- .../course-users-selector.component.spec.ts | 55 +- .../shared/date-time-picker.component.spec.ts | 5 +- ...-overview-navigation-bar.component.spec.ts | 6 +- .../exercise-update-warning.component.spec.ts | 6 - .../feedback/feedback-modal.component.spec.ts | 10 +- .../feedback/feedback-text.component.spec.ts | 1 - .../shared/fireworks.component.spec.ts | 1 - ...ing-instructions-details.component.spec.ts | 1 - .../image-croppper.component.spec.ts | 1 - .../component/shared/main.component.spec.ts | 10 +- .../answer-post/answer-post.component.spec.ts | 4 +- .../message-inline-input.component.spec.ts | 14 +- ...ssage-reply-inline-input.component.spec.ts | 11 +- .../posting-content-part.component.spec.ts | 14 +- .../post-tag-selector.component.spec.ts | 15 +- .../post-reactions-bar.component.spec.ts | 8 +- .../monaco-editor-action.integration.spec.ts | 2 + .../monaco-editor.service.spec.ts | 1 + .../system-notification.component.spec.ts | 3 +- ...pant-scores-distribution.component.spec.ts | 12 +- .../resizeable-container.component.spec.ts | 6 +- .../shared/science.component.spec.ts | 5 +- .../conversation-options.component.spec.ts | 9 +- .../sidebar-card-large.component.spec.ts | 22 +- .../sidebar-card-medium.component.spec.ts | 22 +- .../sidebar-card-small.component.spec.ts | 22 +- .../treeview/treeview-item.component.spec.ts | 4 +- .../user-settings.directive.spec.ts | 26 +- .../vertical-progress-bar.component.spec.ts | 6 +- ...ort-answer-question-edit.component.spec.ts | 10 +- ...n-import-standardized-competencies.spec.ts | 9 +- ...d-competency-filter-page.component.spec.ts | 1 - ...standardized-competency-management.spec.ts | 44 +- .../doughnut-chart.component.spec.ts | 9 +- ...tics-average-score-graph.component.spec.ts | 11 +- .../statistics-graph.component.spec.ts | 10 +- ...score-distribution-graph.component.spec.ts | 9 +- .../table-editable-field.component.spec.ts | 5 +- .../team-exercise-search.component.spec.ts | 4 - .../team/team-owner-search.component.spec.ts | 7 +- ...team-participation-table.component.spec.ts | 21 +- .../component/team/team.component.spec.ts | 36 +- .../teams-export-button.component.spec.ts | 4 +- .../teams-import-button.component.spec.ts | 4 +- .../teams-import-dialog.component.spec.ts | 24 +- ...ms-import-from-file-form.component.spec.ts | 9 +- .../component/team/teams.component.spec.ts | 14 +- .../text-editor/text-editor.component.spec.ts | 2 - .../example-text-submission.component.spec.ts | 4 +- .../text-exercise-detail.component.spec.ts | 4 +- ...ext-exercise-row-buttons.component.spec.ts | 5 +- .../text-exercise-update.component.spec.ts | 8 +- .../text-exercise.component.spec.ts | 8 +- .../tutor-effort-statistics.component.spec.ts | 15 +- ...extblock-assessment-card.component.spec.ts | 2 - ...extblock-feedback-editor.component.spec.ts | 2 - .../manual-text-selection.component.spec.ts | 5 - .../tutor-leaderboard.component.spec.ts | 24 +- ...se-tutorial-group-detail.component.spec.ts | 3 - .../helpers/tutorialGroupExampleModels.ts | 5 +- .../shared/meeting-pattern.pipe.spec.ts | 7 +- .../tutorial-group-detail.component.spec.ts | 1 + ...group-free-days-overview.component.spec.ts | 3 +- ...ial-group-sessions-table.component.spec.ts | 10 +- .../tutorial-groups-table.component.spec.ts | 22 +- ...ial-group-sessions-table-stub.component.ts | 1 - .../tutorial-groups-table-stub.component.ts | 1 - .../registered-students.component.spec.ts | 3 +- ...torial-group-free-period.component.spec.ts | 39 +- ...torial-group-free-period.component.spec.ts | 25 +- ...-free-period-row-buttons.component.spec.ts | 2 + ...-free-periods-management.component.spec.ts | 13 +- ...e-tutorial-group-session.component.spec.ts | 38 +- ...t-tutorial-group-session.component.spec.ts | 42 +- .../cancellation-modal.component.spec.ts | 9 +- ...roup-session-row-buttons.component.spec.ts | 18 +- ...torial-group-session-row.component.spec.ts | 7 +- ...roup-sessions-management.component.spec.ts | 28 +- ...ial-groups-configuration.component.spec.ts | 47 +- ...ial-groups-configuration.component.spec.ts | 44 +- .../create-tutorial-group.component.spec.ts | 32 +- .../edit-tutorial-group.component.spec.ts | 62 +- .../tutorial-group-form.component.spec.ts | 34 +- ...-group-management-detail.component.spec.ts | 7 +- ...utorial-group-row-button.component.spec.ts | 43 +- ...utorial-groups-checklist.component.spec.ts | 3 + ...roups-course-information.component.spec.ts | 3 + ...ial-groups-export-button.component.spec.ts | 3 + ...ial-groups-import-button.component.spec.ts | 8 +- ...torial-groups-management.component.spec.ts | 96 +- .../utils/item-count.component.spec.ts | 1 - .../spec/component/utils/result.utils.spec.ts | 1 + .../virtual-scroll.component.spec.ts | 1 - ...custom-pattern-validator.directive.spec.ts | 16 +- .../directive/delete-dialog.directive.spec.ts | 13 +- .../spec/directive/fit-text.directive.spec.ts | 5 +- .../directive/orion-filter.directive.spec.ts | 7 +- .../spec/directive/posting.directive.spec.ts | 1 - .../spec/directive/science.directive.spec.ts | 2 +- .../directive/sidebar-card.directive.spec.ts | 3 +- .../sticky-popover.directive.spec.ts | 4 +- .../mock-has-any-authority.directive.ts | 2 +- .../mock-jhi-translate-directive.directive.ts | 1 - .../directive/mock-router-link.directive.ts | 3 - .../mock-translate-values.directive.ts | 1 - .../mocks/directive/ngbAlertsMocks.module.ts | 1 - .../directive/ngbCollapseMocks.module.ts | 4 +- .../directive/ngbDropdownMocks.module.ts | 7 +- .../directive/ngbPaginationMocks.module.ts | 17 +- .../mocks/directive/ngbTooltipMocks.module.ts | 19 +- .../mocks/service/mock-clipboard-item.ts | 2 + .../mocks/service/mock-ngb-modal.service.ts | 2 +- .../mocks/service/mock-translate.service.ts | 3 +- .../spec/helpers/sample/problemStatement.json | 4 +- .../code-editor-container.integration.spec.ts | 46 +- .../guided-tour.integration.spec.ts | 138 +- .../artemis-version.interceptor.spec.ts | 55 +- .../auth-expired.interceptor.spec.ts | 17 +- .../browser-fingerprint.interceptor.spec.ts | 39 +- .../errorhandler.interceptor.spec.ts | 7 +- .../notification.interceptor.spec.ts | 7 +- .../spec/pipe/artemis-date.pipe.spec.ts | 8 +- .../spec/pipe/artemis-time-ago.pipe.spec.ts | 24 +- .../reacting-users-on-posting.pipe.spec.ts | 4 +- .../service/delete-dialog.service.spec.ts | 2 - .../feedback/feedback-item-service.spec.ts | 11 +- .../programming-feedback-item.service.spec.ts | 11 +- .../spec/service/metis/metis.service.spec.ts | 1 + .../spec/service/notification.service.spec.ts | 3 +- .../orion/orion-connector.service.spec.ts | 4 - ...rcise-instruction-analysis.service.spec.ts | 12 +- .../spec/service/science.service.spec.ts | 7 +- .../task-count-warning.component.spec.ts | 1 - .../spec/service/theme.service.spec.ts | 4 +- .../service/user-route-access.service.spec.ts | 5 - src/test/javascript/spec/test.module.ts | 1 - .../util/shared/sort-by.directive.spec.ts | 10 +- .../spec/util/shared/sort.directive.spec.ts | 5 +- .../util/shared/translate.directive.spec.ts | 28 +- .../javascript/vitest/main.component.spec.ts | 8 +- tsconfig.json | 7 +- 1718 files changed, 16620 insertions(+), 14208 deletions(-) create mode 100644 patches/ng-mocks+14.13.1.patch delete mode 100644 src/main/webapp/app/app-routing.module.ts rename src/main/webapp/app/{shared/layouts/main/main.component.html => app.component.html} (100%) rename src/main/webapp/app/{shared/layouts/main/main.component.scss => app.component.scss} (100%) rename src/main/webapp/app/{shared/layouts/main/main.component.ts => app.component.ts} (76%) create mode 100644 src/main/webapp/app/app.config.ts delete mode 100644 src/main/webapp/app/app.module.ts create mode 100644 src/main/webapp/app/app.routes.ts delete mode 100644 src/main/webapp/app/core/core.module.ts rename src/main/webapp/app/exam/shared/testExam-workingTime/{testexam-working-time.component.html => test-exam-working-time.component.html} (100%) rename src/main/webapp/app/exam/shared/testExam-workingTime/{testexam-working-time.component.ts => test-exam-working-time.component.ts} (81%) delete mode 100644 src/main/webapp/app/exercises/quiz/shared/fit-text/fit-text.module.ts delete mode 100644 src/main/webapp/app/exercises/shared/team-submission-sync/team-submission-sync.module.ts delete mode 100644 src/main/webapp/app/exercises/text/shared/text-shared.module.ts rename src/main/webapp/app/forms/{forms.module.ts => artemis-forms.module.ts} (73%) rename src/main/webapp/{content => app}/icons/icons.ts (100%) delete mode 100644 src/main/webapp/app/shared/feature-toggle/feature-toggle.module.ts delete mode 100644 src/main/webapp/app/shared/fireworks/fireworks.module.ts delete mode 100644 src/main/webapp/app/shared/image-cropper/image-cropper.module.ts delete mode 100644 src/main/webapp/app/shared/shared-libs.module.ts delete mode 100644 src/main/webapp/app/shared/vertical-progress-bar/vertical-progress-bar.module.ts delete mode 100644 src/main/webapp/app/shared/virtual-scroll/virtual-scroll.module.ts diff --git a/angular.json b/angular.json index 24f70a21a733..bb1e8e3817f7 100644 --- a/angular.json +++ b/angular.json @@ -18,7 +18,7 @@ "prefix": "jhi", "architect": { "build": { - "builder": "@angular-devkit/build-angular:application", + "builder": "@angular/build:application", "options": { "allowedCommonJsDependencies": [ "@vscode/markdown-it-katex", @@ -187,7 +187,7 @@ "defaultConfiguration": "production" }, "serve": { - "builder": "@angular-devkit/build-angular:dev-server", + "builder": "@angular/build:dev-server", "options": { "buildTarget": "artemis:build:development", "proxyConfig": "proxy.conf.mjs", diff --git a/eslint.config.js b/eslint.config.js index 6029bf30bcd8..045271a554d2 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -51,22 +51,6 @@ module.exports = [ ...tsPlugin.configs.recommended.rules, ...angularPlugin.configs.recommended.rules, 'custom-rules/enforce-no-http-client-testing-module': 'error', - '@angular-eslint/directive-selector': [ - 'warn', - { - type: 'attribute', - prefix: 'jhi', - style: 'camelCase', - }, - ], - '@angular-eslint/component-selector': [ - 'warn', - { - type: 'element', - prefix: 'jhi', - style: 'kebab-case', - }, - ], '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/no-unsafe-member-access': 'off', diff --git a/jest.config.js b/jest.config.js index ab2c159af355..d036d9fb7f03 100644 --- a/jest.config.js +++ b/jest.config.js @@ -105,10 +105,10 @@ module.exports = { coverageThreshold: { global: { // TODO: in the future, the following values should increase to at least 90% - statements: 87.81, - branches: 73.97, - functions: 82.50, - lines: 87.86, + statements: 88.18, + branches: 74.20, + functions: 81.09, + lines: 88.46, }, }, coverageReporters: ['clover', 'json', 'lcov', 'text-summary'], diff --git a/package-lock.json b/package-lock.json index a543cb1fb222..6ee54f8eef54 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,30 +10,30 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@angular/animations": "18.2.13", - "@angular/cdk": "18.2.14", - "@angular/common": "18.2.13", - "@angular/compiler": "18.2.13", - "@angular/core": "18.2.13", - "@angular/forms": "18.2.13", - "@angular/localize": "18.2.13", - "@angular/material": "18.2.14", - "@angular/platform-browser": "18.2.13", - "@angular/platform-browser-dynamic": "18.2.13", - "@angular/router": "18.2.13", - "@angular/service-worker": "18.2.13", + "@angular/animations": "19.0.6", + "@angular/cdk": "19.0.5", + "@angular/common": "19.0.6", + "@angular/compiler": "19.0.6", + "@angular/core": "19.0.6", + "@angular/forms": "19.0.6", + "@angular/localize": "19.0.6", + "@angular/material": "19.0.5", + "@angular/platform-browser": "19.0.6", + "@angular/platform-browser-dynamic": "19.0.6", + "@angular/router": "19.0.6", + "@angular/service-worker": "19.0.6", "@ctrl/ngx-emoji-mart": "9.2.0", - "@danielmoncada/angular-datetime-picker": "18.1.0", + "@danielmoncada/angular-datetime-picker": "19.0.0", "@fingerprintjs/fingerprintjs": "4.5.1", - "@fortawesome/angular-fontawesome": "0.15.0", + "@fortawesome/angular-fontawesome": "1.0.0", "@fortawesome/fontawesome-svg-core": "6.7.2", "@fortawesome/free-regular-svg-icons": "6.7.2", "@fortawesome/free-solid-svg-icons": "6.7.2", "@ls1intum/apollon": "3.3.15", - "@ng-bootstrap/ng-bootstrap": "17.0.1", + "@ng-bootstrap/ng-bootstrap": "18.0.0", "@ngx-translate/core": "16.0.4", "@ngx-translate/http-loader": "16.0.1", - "@sentry/angular": "8.47.0", + "@sentry/angular": "8.48.0", "@siemens/ngx-datatable": "22.4.1", "@swimlane/ngx-charts": "21.1.2", "@swimlane/ngx-graph": "9.0.1", @@ -41,7 +41,7 @@ "@vscode/markdown-it-katex": "1.1.1", "bootstrap": "5.3.3", "compare-versions": "6.1.1", - "core-js": "3.39.0", + "core-js": "3.40.0", "crypto-js": "4.2.0", "dayjs": "1.11.13", "diff-match-patch-typescript": "1.1.0", @@ -61,9 +61,9 @@ "markdown-it-highlightjs": "4.2.0", "mobile-drag-drop": "3.0.0-rc.0", "monaco-editor": "0.52.2", - "ngx-infinite-scroll": "18.0.0", - "ngx-webstorage": "18.0.0", - "papaparse": "5.4.1", + "ngx-infinite-scroll": "19.0.0", + "ngx-webstorage": "19.0.1", + "papaparse": "5.5.1", "pdf-lib": "1.17.1", "pdfjs-dist": "4.10.38", "rxjs": "7.8.1", @@ -74,27 +74,27 @@ "ts-cacheable": "1.0.10", "tslib": "2.8.1", "turndown": "7.2.0", - "uuid": "11.0.4", + "uuid": "11.0.5", "webstomp-client": "1.2.6", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", - "zone.js": "0.14.10" + "zone.js": "0.15.0" }, "devDependencies": { - "@analogjs/vite-plugin-angular": "1.11.0", - "@angular-builders/jest": "18.0.0", - "@angular-devkit/build-angular": "18.2.12", - "@angular-eslint/builder": "18.4.3", - "@angular-eslint/eslint-plugin": "18.4.3", - "@angular-eslint/eslint-plugin-template": "18.4.3", - "@angular-eslint/schematics": "18.4.3", - "@angular-eslint/template-parser": "18.4.3", - "@angular/cli": "18.2.12", - "@angular/compiler-cli": "18.2.13", - "@angular/language-service": "18.2.13", - "@sentry/types": "8.47.0", + "@analogjs/vite-plugin-angular": "1.12.0", + "@angular-builders/jest": "19.0.0", + "@angular-eslint/builder": "19.0.2", + "@angular-eslint/eslint-plugin": "19.0.2", + "@angular-eslint/eslint-plugin-template": "19.0.2", + "@angular-eslint/schematics": "19.0.2", + "@angular-eslint/template-parser": "19.0.2", + "@angular/build": "19.0.7", + "@angular/cli": "19.0.7", + "@angular/compiler-cli": "19.0.6", + "@angular/language-service": "19.0.6", + "@sentry/types": "8.48.0", "@testing-library/angular": "17.3.5", "@types/crypto-js": "4.2.2", - "@types/d3-shape": "3.1.6", + "@types/d3-shape": "3.1.7", "@types/dompurify": "3.0.5", "@types/emoji-js": "3.5.2", "@types/jest": "29.5.14", @@ -106,15 +106,15 @@ "@types/sockjs-client": "1.5.4", "@types/turndown": "5.0.5", "@types/uuid": "10.0.0", - "@typescript-eslint/eslint-plugin": "8.19.0", - "@typescript-eslint/parser": "8.19.0", - "eslint": "9.17.0", + "@typescript-eslint/eslint-plugin": "8.19.1", + "@typescript-eslint/parser": "8.19.1", + "eslint": "9.18.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-deprecation": "3.0.0", "eslint-plugin-jest": "28.10.0", "eslint-plugin-jest-extended": "2.4.0", "eslint-plugin-prettier": "5.2.1", - "folder-hash": "4.1.0", + "folder-hash": "4.1.1", "husky": "9.1.7", "jest": "29.7.0", "jest-canvas-mock": "2.5.2", @@ -122,7 +122,7 @@ "jest-extended": "4.0.2", "jest-fail-on-console": "3.3.1", "jest-junit": "16.0.0", - "jest-preset-angular": "14.4.2", + "jest-preset-angular": "14.5.0", "lint-staged": "15.3.0", "ng-mocks": "14.13.1", "ngxtension": "4.2.0", @@ -131,8 +131,8 @@ "rimraf": "6.0.1", "sass": "1.83.1", "ts-jest": "29.2.5", - "typescript": "5.5.4", - "typescript-eslint": "8.19.0", + "typescript": "5.6.3", + "typescript-eslint": "8.19.1", "vite-tsconfig-paths": "5.1.4", "vitest": "2.1.8", "weak-napi": "2.0.2" @@ -155,9 +155,9 @@ } }, "node_modules/@analogjs/vite-plugin-angular": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@analogjs/vite-plugin-angular/-/vite-plugin-angular-1.11.0.tgz", - "integrity": "sha512-18HSwOAVFQjwwRQPq9+duOoubuiut0INo55h0gV3v2TWS+tAP2wXP8SPyAG99P3ySNQB7zMUYE8mVsqLM+8bDA==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@analogjs/vite-plugin-angular/-/vite-plugin-angular-1.12.0.tgz", + "integrity": "sha512-D/INewdBlO24eYs1wvau9uaEsIg74GqSYx457SL/wHw3WTtqagCVPcOhAI4UlnIryjw1EAvUQiwbgFnMo+bYfQ==", "dev": true, "license": "MIT", "dependencies": { @@ -182,41 +182,41 @@ } }, "node_modules/@angular-builders/common": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@angular-builders/common/-/common-2.0.0.tgz", - "integrity": "sha512-O5YJc++DtJVJhqA/OomRKN2jGYzvU/YXtfrPAqcA9Is3Ob5jvV0L0JHSAjSw/KaLvk/FjBIqoRVcYdLp5LKddA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@angular-builders/common/-/common-3.0.0.tgz", + "integrity": "sha512-AACGMwlBFYF3PaFekgJDCmqO1hMBrK5eyjHMN5aqJk3PV46BhnlNcQEa9pftLUKxoGijXBQzlalDZkceatyoMw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "^18.0.0", + "@angular-devkit/core": "^19.0.0", "ts-node": "^10.0.0", "tsconfig-paths": "^4.1.0" }, "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" } }, "node_modules/@angular-builders/jest": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@angular-builders/jest/-/jest-18.0.0.tgz", - "integrity": "sha512-nPNWMlV1ryjir6CC8dY8f6yaNe0lM1VJwbvYM99HmJSeRo+1IwouQQGUjRM/2Dx4OMAmL4EAPh7Ud2nr3Gj/FA==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@angular-builders/jest/-/jest-19.0.0.tgz", + "integrity": "sha512-DEKragHT26kwUhXx9goYehQ/WxFzpVrMHIicYF+L7sLVmFyCwPwAslYZZBe/eTm/x++tKRHnun1lbcf1ZDccRg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-builders/common": "2.0.0", - "@angular-devkit/architect": ">=0.1800.0 < 0.1900.0", - "@angular-devkit/core": "^18.0.0", - "jest-preset-angular": "14.1.0", + "@angular-builders/common": "3.0.0", + "@angular-devkit/architect": ">=0.1900.0 < 0.2000.0", + "@angular-devkit/core": "^19.0.0", + "jest-preset-angular": "14.4.2", "lodash": "^4.17.15" }, "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular-devkit/build-angular": "^18.0.0", - "@angular/compiler-cli": "^18.0.0", - "@angular/core": "^18.0.0", - "@angular/platform-browser-dynamic": "^18.0.0", + "@angular-devkit/build-angular": "^19.0.0", + "@angular/compiler-cli": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/platform-browser-dynamic": "^19.0.0", "jest": ">=29" } }, @@ -234,9 +234,9 @@ } }, "node_modules/@angular-builders/jest/node_modules/jest-preset-angular": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.1.0.tgz", - "integrity": "sha512-UJwPtpsAMl30UtBjHW0Ai0hhoKsNURC1dXH5tSYjumUsWR7iDke+oBEykz7uXv4rN+PWgeNIqkxo4KHQjOITlw==", + "version": "14.4.2", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.4.2.tgz", + "integrity": "sha512-BYYv0FaTDfBNh8WyA9mpOV3krfw20kurBGK8INZUnv7KZDAWZuQtCET4TwTWxSNQ9jS1OX1+a5weCm/bTDDM1A==", "dev": true, "license": "MIT", "dependencies": { @@ -254,10 +254,9 @@ "esbuild": ">=0.15.13" }, "peerDependencies": { - "@angular-devkit/build-angular": ">=15.0.0 <19.0.0", - "@angular/compiler-cli": ">=15.0.0 <19.0.0", - "@angular/core": ">=15.0.0 <19.0.0", - "@angular/platform-browser-dynamic": ">=15.0.0 <19.0.0", + "@angular/compiler-cli": ">=15.0.0 <20.0.0", + "@angular/core": ">=15.0.0 <20.0.0", + "@angular/platform-browser-dynamic": ">=15.0.0 <20.0.0", "jest": "^29.0.0", "typescript": ">=4.8" } @@ -285,13 +284,13 @@ "license": "MIT" }, "node_modules/@angular-devkit/architect": { - "version": "0.1802.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.12.tgz", - "integrity": "sha512-bepVb2/GtJppYKaeW8yTGE6egmoWZ7zagFDsmBdbF+BYp+HmeoPsclARcdryBPVq68zedyTRdvhWSUTbw1AYuw==", + "version": "0.1900.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1900.7.tgz", + "integrity": "sha512-3dRV0IB+MbNYbAGbYEFMcABkMphqcTvn5MG79dQkwcf2a9QZxCq2slwf/rIleWoDUcFm9r1NnVPYrTYNYJaqQg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.12", + "@angular-devkit/core": "19.0.7", "rxjs": "7.8.1" }, "engines": { @@ -301,40 +300,39 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.12.tgz", - "integrity": "sha512-quVUi7eqTq9OHumQFNl9Y8t2opm8miu4rlYnuF6rbujmmBDvdUvR6trFChueRczl2p5HWqTOr6NPoDGQm8AyNw==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-19.0.7.tgz", + "integrity": "sha512-R0vpJ+P5xBqF82zOMq2FvOP7pJz5NZ7PwHAIFuQ6z50SHLW/VcUA19ZoFKwxBX6A/Soyb66QXTcjZ5wbRqMm8w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.12", - "@angular-devkit/build-webpack": "0.1802.12", - "@angular-devkit/core": "18.2.12", - "@angular/build": "18.2.12", - "@babel/core": "7.25.2", - "@babel/generator": "7.25.0", - "@babel/helper-annotate-as-pure": "7.24.7", + "@angular-devkit/architect": "0.1900.7", + "@angular-devkit/build-webpack": "0.1900.7", + "@angular-devkit/core": "19.0.7", + "@angular/build": "19.0.7", + "@babel/core": "7.26.0", + "@babel/generator": "7.26.2", + "@babel/helper-annotate-as-pure": "7.25.9", "@babel/helper-split-export-declaration": "7.24.7", - "@babel/plugin-transform-async-generator-functions": "7.25.0", - "@babel/plugin-transform-async-to-generator": "7.24.7", - "@babel/plugin-transform-runtime": "7.24.7", - "@babel/preset-env": "7.25.3", - "@babel/runtime": "7.25.0", - "@discoveryjs/json-ext": "0.6.1", - "@ngtools/webpack": "18.2.12", + "@babel/plugin-transform-async-generator-functions": "7.25.9", + "@babel/plugin-transform-async-to-generator": "7.25.9", + "@babel/plugin-transform-runtime": "7.25.9", + "@babel/preset-env": "7.26.0", + "@babel/runtime": "7.26.0", + "@discoveryjs/json-ext": "0.6.3", + "@ngtools/webpack": "19.0.7", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", - "babel-loader": "9.1.3", + "babel-loader": "9.2.1", "browserslist": "^4.21.5", "copy-webpack-plugin": "12.0.2", - "critters": "0.0.24", "css-loader": "7.1.2", - "esbuild-wasm": "0.23.0", + "esbuild-wasm": "0.24.0", "fast-glob": "3.3.2", "http-proxy-middleware": "3.0.3", - "https-proxy-agent": "7.0.5", "istanbul-lib-instrument": "6.0.3", "jsonc-parser": "3.3.1", "karma-source-map-support": "1.4.0", @@ -342,31 +340,26 @@ "less-loader": "12.2.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.3.1", - "magic-string": "0.30.11", - "mini-css-extract-plugin": "2.9.0", - "mrmime": "2.0.0", + "mini-css-extract-plugin": "2.9.2", "open": "10.1.0", "ora": "5.4.1", - "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.2", - "piscina": "4.6.1", - "postcss": "8.4.41", + "piscina": "4.7.0", + "postcss": "8.4.49", "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.77.6", - "sass-loader": "16.0.0", + "sass": "1.80.7", + "sass-loader": "16.0.3", "semver": "7.6.3", "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.31.6", + "terser": "5.36.0", "tree-kill": "1.2.2", - "tslib": "2.6.3", - "vite": "5.4.6", - "watchpack": "2.4.1", - "webpack": "5.94.0", + "tslib": "2.8.1", + "webpack": "5.96.1", "webpack-dev-middleware": "7.4.2", - "webpack-dev-server": "5.0.4", + "webpack-dev-server": "5.1.0", "webpack-merge": "6.0.1", "webpack-subresource-integrity": "5.1.0" }, @@ -376,22 +369,23 @@ "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.23.0" + "esbuild": "0.24.0" }, "peerDependencies": { - "@angular/compiler-cli": "^18.0.0", - "@angular/localize": "^18.0.0", - "@angular/platform-server": "^18.0.0", - "@angular/service-worker": "^18.0.0", - "@web/test-runner": "^0.18.0", + "@angular/compiler-cli": "^19.0.0", + "@angular/localize": "^19.0.0", + "@angular/platform-server": "^19.0.0", + "@angular/service-worker": "^19.0.0", + "@angular/ssr": "^19.0.7", + "@web/test-runner": "^0.19.0", "browser-sync": "^3.0.2", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "karma": "^6.3.0", - "ng-packagr": "^18.0.0", + "ng-packagr": "^19.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.4 <5.6" + "typescript": ">=5.5 <5.7" }, "peerDependenciesMeta": { "@angular/localize": { @@ -403,6 +397,9 @@ "@angular/service-worker": { "optional": true }, + "@angular/ssr": { + "optional": true + }, "@web/test-runner": { "optional": true }, @@ -429,22 +426,16 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "dev": true, - "license": "MIT" - }, "node_modules/@angular-devkit/build-angular/node_modules/sass": { - "version": "1.77.6", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", - "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "version": "1.80.7", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.7.tgz", + "integrity": "sha512-MVWvN0u5meytrSjsU7AWsbhoXi1sc58zADXFllfZzbsBT1GHjjar6JwBINYPRrkx/zqnQ6uqbQuHgE95O+C+eQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", + "chokidar": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -452,23 +443,20 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, - "node_modules/@angular-devkit/build-angular/node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true, - "license": "0BSD" - }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1802.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.12.tgz", - "integrity": "sha512-0Z3fdbZVRnjYWE2/VYyfy+uieY+6YZyEp4ylzklVkc+fmLNsnz4Zw6cK1LzzcBqAwKIyh1IdW20Cg7o8b0sONA==", + "version": "0.1900.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1900.7.tgz", + "integrity": "sha512-F0S0iyspo/9w9rP5F9wmL+ZkBr48YQIWiFu+PaQ0in/lcdRmY/FjVHTMa5BMnlew9VCtFHPvpoN9x4u8AIoWXA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@angular-devkit/architect": "0.1802.12", + "@angular-devkit/architect": "0.1900.7", "rxjs": "7.8.1" }, "engines": { @@ -482,9 +470,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.12.tgz", - "integrity": "sha512-NtB6ypsaDyPE6/fqWOdfTmACs+yK5RqfH5tStEzWFeeDsIEDYKsJ06ypuRep7qTjYus5Rmttk0Ds+cFgz8JdUQ==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.0.7.tgz", + "integrity": "sha512-VyuORSitT6LIaGUEF0KEnv2TwNaeWl6L3/4L4stok0BJ23B4joVca2DYVcrLC1hSzz8V4dwVgSlbNIgjgGdVpg==", "dev": true, "license": "MIT", "dependencies": { @@ -501,7 +489,7 @@ "yarn": ">= 1.13.0" }, "peerDependencies": { - "chokidar": "^3.5.2" + "chokidar": "^4.0.0" }, "peerDependenciesMeta": { "chokidar": { @@ -510,15 +498,15 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.12.tgz", - "integrity": "sha512-mMea9txHbnCX5lXLHlo0RAgfhFHDio45/jMsREM2PA8UtVf2S8ltXz7ZwUrUyMQRv8vaSfn4ijDstF4hDMnRgQ==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.0.7.tgz", + "integrity": "sha512-BHXQv6kMc9xo4TH9lhwMv8nrZXHkLioQvLun2qYjwvOsyzt3qd+sUM9wpHwbG6t+01+FIQ05iNN9ox+Cvpndgg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.12", + "@angular-devkit/core": "19.0.7", "jsonc-parser": "3.3.1", - "magic-string": "0.30.11", + "magic-string": "0.30.12", "ora": "5.4.1", "rxjs": "7.8.1" }, @@ -529,14 +517,14 @@ } }, "node_modules/@angular-eslint/builder": { - "version": "18.4.3", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-18.4.3.tgz", - "integrity": "sha512-NzmrXlr7GFE+cjwipY/CxBscZXNqnuK0us1mO6Z2T6MeH6m+rRcdlY/rZyKoRniyNNvuzl6vpEsfMIMmnfebrA==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-19.0.2.tgz", + "integrity": "sha512-BdmMSndQt2fSBiTVniskUcUpQaeweUapbsL0IDfQ7a13vL0NVXpc3K89YXuVE/xsb08uHtqphuwxPAAj6kX3OA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": ">= 0.1800.0 < 0.1900.0", - "@angular-devkit/core": ">= 18.0.0 < 19.0.0" + "@angular-devkit/architect": ">= 0.1900.0 < 0.2000.0", + "@angular-devkit/core": ">= 19.0.0 < 20.0.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", @@ -544,21 +532,21 @@ } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "18.4.3", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-18.4.3.tgz", - "integrity": "sha512-zdrA8mR98X+U4YgHzUKmivRU+PxzwOL/j8G7eTOvBuq8GPzsP+hvak+tyxlgeGm9HsvpFj9ERHLtJ0xDUPs8fg==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-19.0.2.tgz", + "integrity": "sha512-HPmp92r70SNO/0NdIaIhxrgVSpomqryuUk7jszvNRtu+OzYCJGcbLhQD38T3dbBWT/AV0QXzyzExn6/2ai9fEw==", "dev": true, "license": "MIT" }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "18.4.3", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-18.4.3.tgz", - "integrity": "sha512-AyJbupiwTBR81P6T59v+aULEnPpZBCBxL2S5QFWfAhNCwWhcof4GihvdK2Z87yhvzDGeAzUFSWl/beJfeFa+PA==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-19.0.2.tgz", + "integrity": "sha512-DLuNVVGGFicSThOcMSJyNje+FZSPdG0B3lCBRiqcgKH/16kfM4pV8MobPM7RGK2NhaOmmZ4zzJNwpwWPSgi+Lw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.4.3", - "@angular-eslint/utils": "18.4.3" + "@angular-eslint/bundled-angular-compiler": "19.0.2", + "@angular-eslint/utils": "19.0.2" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -567,14 +555,14 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "18.4.3", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-18.4.3.tgz", - "integrity": "sha512-ijGlX2N01ayMXTpeQivOA31AszO8OEbu9ZQUCxnu9AyMMhxyi2q50bujRChAvN9YXQfdQtbxuajxV6+aiWb5BQ==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-19.0.2.tgz", + "integrity": "sha512-f/OCF9ThnxQ8m0eNYPwnCrySQPhYfCOF6STL7F9LnS8Bs3ZeW3/oT1yLaMIZ1Eg0ogIkgxksMAJZjrJPUPBD1Q==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.4.3", - "@angular-eslint/utils": "18.4.3", + "@angular-eslint/bundled-angular-compiler": "19.0.2", + "@angular-eslint/utils": "19.0.2", "aria-query": "5.3.2", "axobject-query": "4.1.0" }, @@ -586,29 +574,29 @@ } }, "node_modules/@angular-eslint/schematics": { - "version": "18.4.3", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-18.4.3.tgz", - "integrity": "sha512-D5maKn5e6n58+8n7jLFLD4g+RGPOPeDSsvPc1sqial5tEKLxAJQJS9WZ28oef3bhkob6C60D+1H0mMmEEVvyVA==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-19.0.2.tgz", + "integrity": "sha512-wI4SyiAnUCrpigtK6PHRlVWMC9vWljqmlLhbsJV5O5yDajlmRdvgXvSHDefhJm0hSfvZYRXuiAARYv2+QVfnGA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": ">= 18.0.0 < 19.0.0", - "@angular-devkit/schematics": ">= 18.0.0 < 19.0.0", - "@angular-eslint/eslint-plugin": "18.4.3", - "@angular-eslint/eslint-plugin-template": "18.4.3", + "@angular-devkit/core": ">= 19.0.0 < 20.0.0", + "@angular-devkit/schematics": ">= 19.0.0 < 20.0.0", + "@angular-eslint/eslint-plugin": "19.0.2", + "@angular-eslint/eslint-plugin-template": "19.0.2", "ignore": "6.0.2", "semver": "7.6.3", "strip-json-comments": "3.1.1" } }, "node_modules/@angular-eslint/template-parser": { - "version": "18.4.3", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-18.4.3.tgz", - "integrity": "sha512-JZMPtEB8yNip3kg4WDEWQyObSo2Hwf+opq2ElYuwe85GQkGhfJSJ2CQYo4FSwd+c5MUQAqESNRg9QqGYauDsiw==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.0.2.tgz", + "integrity": "sha512-z3rZd2sBfuYcFf9rGDsB2zz2fbGX8kkF+0ftg9eocyQmzWrlZHFmuw9ha7oP/Mz8gpblyCS/aa1U/Srs6gz0UQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.4.3", + "@angular-eslint/bundled-angular-compiler": "19.0.2", "eslint-scope": "^8.0.2" }, "peerDependencies": { @@ -617,13 +605,13 @@ } }, "node_modules/@angular-eslint/utils": { - "version": "18.4.3", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-18.4.3.tgz", - "integrity": "sha512-w0bJ9+ELAEiPBSTPPm9bvDngfu1d8JbzUhvs2vU+z7sIz/HMwUZT5S4naypj2kNN0gZYGYrW0lt+HIbW87zTAQ==", + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.0.2.tgz", + "integrity": "sha512-HotBT8OKr7zCaX1S9k27JuhRiTVIbbYVl6whlb3uwdMIPIWY8iOcEh1tjI4qDPUafpLfR72Dhwi5bO1E17F3/Q==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "18.4.3" + "@angular-eslint/bundled-angular-compiler": "19.0.2" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -632,9 +620,9 @@ } }, "node_modules/@angular/animations": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.13.tgz", - "integrity": "sha512-rG5J5Ek5Hg+Tz2NjkNOaG6PupiNK/lPfophXpsR1t/nWujqnMWX2krahD/i6kgD+jNWNKCJCYSOVvCx/BHOtKA==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-19.0.6.tgz", + "integrity": "sha512-dlXrFcw7RQNze1zjmrbwqcFd6zgEuqKwuExtEN1Fy26kQ+wqKIhYO6IG7PZGef53XpwN5DT16yve6UihJ2XeNg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -643,56 +631,61 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.13" + "@angular/core": "19.0.6" } }, "node_modules/@angular/build": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.12.tgz", - "integrity": "sha512-4Ohz+OSILoL+cCAQ4UTiCT5v6pctu3fXNoNpTEUK46OmxELk9jDITO5rNyNS7TxBn9wY69kjX5VcDf7MenquFQ==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-19.0.7.tgz", + "integrity": "sha512-AFvhRa6sfXG8NmS8AN7TvE8q2kVcMw+zXMZzo981cqwnOwJy4VHU0htqm5OZQnohVJM0pP8SBAuROWO4yRrxCA==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.12", - "@babel/core": "7.25.2", - "@babel/helper-annotate-as-pure": "7.24.7", + "@angular-devkit/architect": "0.1900.7", + "@babel/core": "7.26.0", + "@babel/helper-annotate-as-pure": "7.25.9", "@babel/helper-split-export-declaration": "7.24.7", - "@babel/plugin-syntax-import-attributes": "7.24.7", - "@inquirer/confirm": "3.1.22", + "@babel/plugin-syntax-import-attributes": "7.26.0", + "@inquirer/confirm": "5.0.2", "@vitejs/plugin-basic-ssl": "1.1.0", + "beasties": "0.1.0", "browserslist": "^4.23.0", - "critters": "0.0.24", - "esbuild": "0.23.0", + "esbuild": "0.24.0", "fast-glob": "3.3.2", "https-proxy-agent": "7.0.5", - "listr2": "8.2.4", - "lmdb": "3.0.13", - "magic-string": "0.30.11", + "istanbul-lib-instrument": "6.0.3", + "listr2": "8.2.5", + "magic-string": "0.30.12", "mrmime": "2.0.0", "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.2", - "piscina": "4.6.1", - "rollup": "4.22.4", - "sass": "1.77.6", + "piscina": "4.7.0", + "rollup": "4.26.0", + "sass": "1.80.7", "semver": "7.6.3", - "vite": "5.4.6", - "watchpack": "2.4.1" + "vite": "5.4.11", + "watchpack": "2.4.2" }, "engines": { "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, + "optionalDependencies": { + "lmdb": "3.1.5" + }, "peerDependencies": { - "@angular/compiler-cli": "^18.0.0", - "@angular/localize": "^18.0.0", - "@angular/platform-server": "^18.0.0", - "@angular/service-worker": "^18.0.0", + "@angular/compiler": "^19.0.0", + "@angular/compiler-cli": "^19.0.0", + "@angular/localize": "^19.0.0", + "@angular/platform-server": "^19.0.0", + "@angular/service-worker": "^19.0.0", + "@angular/ssr": "^19.0.7", "less": "^4.2.0", "postcss": "^8.4.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.4 <5.6" + "typescript": ">=5.5 <5.7" }, "peerDependenciesMeta": { "@angular/localize": { @@ -704,6 +697,9 @@ "@angular/service-worker": { "optional": true }, + "@angular/ssr": { + "optional": true + }, "less": { "optional": true }, @@ -715,22 +711,15 @@ } } }, - "node_modules/@angular/build/node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "dev": true, - "license": "MIT" - }, "node_modules/@angular/build/node_modules/sass": { - "version": "1.77.6", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", - "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", + "version": "1.80.7", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.80.7.tgz", + "integrity": "sha512-MVWvN0u5meytrSjsU7AWsbhoXi1sc58zADXFllfZzbsBT1GHjjar6JwBINYPRrkx/zqnQ6uqbQuHgE95O+C+eQ==", "dev": true, "license": "MIT", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", + "chokidar": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -738,12 +727,15 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, "node_modules/@angular/cdk": { - "version": "18.2.14", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.14.tgz", - "integrity": "sha512-vDyOh1lwjfVk9OqoroZAP8pf3xxKUvyl+TVR8nJxL4c5fOfUFkD7l94HaanqKSRwJcI2xiztuu92IVoHn8T33Q==", + "version": "19.0.5", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.0.5.tgz", + "integrity": "sha512-+D++QUrJlDuwk5RhQBDTejQseb0ZP6c6S4r8wBBab7UPtrwigySudSb0PxhiAzp2YHr5Ch3klhkTf/NSWeUXUQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -752,31 +744,31 @@ "parse5": "^7.1.2" }, "peerDependencies": { - "@angular/common": "^18.0.0 || ^19.0.0", - "@angular/core": "^18.0.0 || ^19.0.0", + "@angular/common": "^19.0.0 || ^20.0.0", + "@angular/core": "^19.0.0 || ^20.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/cli": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.12.tgz", - "integrity": "sha512-xhuZ/b7IhqNw1MgXf+arWf4x+GfUSt/IwbdWU4+CO8A7h0Y46zQywouP/KUK3cMQZfVdHdciTBvlpF3vFacA6Q==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.0.7.tgz", + "integrity": "sha512-y6C4B4XdiZwe2+OADLWXyKqUVvW/XDzTuJ2mZ5PhTnSiiXDN4zRWId1F5wA8ve8vlbUKApPHXRQuaqiQJmA24g==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.12", - "@angular-devkit/core": "18.2.12", - "@angular-devkit/schematics": "18.2.12", - "@inquirer/prompts": "5.3.8", - "@listr2/prompt-adapter-inquirer": "2.0.15", - "@schematics/angular": "18.2.12", + "@angular-devkit/architect": "0.1900.7", + "@angular-devkit/core": "19.0.7", + "@angular-devkit/schematics": "19.0.7", + "@inquirer/prompts": "7.1.0", + "@listr2/prompt-adapter-inquirer": "2.0.18", + "@schematics/angular": "19.0.7", "@yarnpkg/lockfile": "1.1.0", - "ini": "4.1.3", + "ini": "5.0.0", "jsonc-parser": "3.3.1", - "listr2": "8.2.4", - "npm-package-arg": "11.0.3", - "npm-pick-manifest": "9.1.0", - "pacote": "18.0.6", + "listr2": "8.2.5", + "npm-package-arg": "12.0.0", + "npm-pick-manifest": "10.0.0", + "pacote": "20.0.0", "resolve": "1.22.8", "semver": "7.6.3", "symbol-observable": "4.0.0", @@ -792,9 +784,9 @@ } }, "node_modules/@angular/common": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.13.tgz", - "integrity": "sha512-4ZqrNp1PoZo7VNvW+sbSc2CB2axP1sCH2wXl8B0wdjsj8JY1hF1OhuugwhpAHtGxqewed2kCXayE+ZJqSTV4jw==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.0.6.tgz", + "integrity": "sha512-r9IDD0+UGkrQkjyX+pApeDmIJ9INpr1uYlgmmlWNBJCVNr9SKKIVZV60sssgadew6bGynKN9dW4mGsmEzzb5BA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -803,14 +795,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.13", + "@angular/core": "19.0.6", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.13.tgz", - "integrity": "sha512-TzWcrkopyjFF+WeDr2cRe8CcHjU72KfYV3Sm2TkBkcXrkYX5sDjGWrBGrG3hRB4e4okqchrOCvm1MiTdy2vKMA==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.0.6.tgz", + "integrity": "sha512-g8A6QOsiCJnRi5Hz0sASIpRQoAGxEgnjz0JanfrMNRedY4MpdIS1V0AeCSKTsMRlV7tQl3ng2Gse/tsb51HI3Q==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -819,7 +811,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.13" + "@angular/core": "19.0.6" }, "peerDependenciesMeta": { "@angular/core": { @@ -828,12 +820,12 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.13.tgz", - "integrity": "sha512-DBSh4AQwkiJDSiVvJATRmjxf6wyUs9pwQLgaFdSlfuTRO+sdb0J2z1r3BYm8t0IqdoyXzdZq2YCH43EmyvD71g==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-19.0.6.tgz", + "integrity": "sha512-fHtwI5rCe3LmKDoaqlqLAPdNmLrbeCiMYVe+X1BHgApaqNCyAwcuJxuf8Q5R5su7nHiLmlmB74o1ZS/V+0cQ+g==", "license": "MIT", "dependencies": { - "@babel/core": "7.25.2", + "@babel/core": "7.26.0", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^4.0.0", "convert-source-map": "^1.5.1", @@ -851,42 +843,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.13", - "typescript": ">=5.4 <5.6" - } - }, - "node_modules/@angular/compiler-cli/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@angular/compiler-cli/node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", - "license": "MIT", - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "@angular/compiler": "19.0.6", + "typescript": ">=5.5 <5.7" } }, "node_modules/@angular/core": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.13.tgz", - "integrity": "sha512-8mbWHMgO95OuFV1Ejy4oKmbe9NOJ3WazQf/f7wks8Bck7pcihd0IKhlPBNjFllbF5o+04EYSwFhEtvEgjMDClA==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.0.6.tgz", + "integrity": "sha512-9N7FmdRHtS7zfXC/wnyap/reX7fgiOrWpVivayHjWP4RkLYXJAzJIpLyew0jrx4vf8r3lZnC0Zmq0PW007Ngjw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -896,13 +860,13 @@ }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.14.10" + "zone.js": "~0.15.0" } }, "node_modules/@angular/forms": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.13.tgz", - "integrity": "sha512-A67D867fu3DSBhdLWWZl/F5pr7v2+dRM2u3U7ZJ0ewh4a+sv+0yqWdJW+a8xIoiHxS+btGEJL2qAKJiH+MCFfg==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.0.6.tgz", + "integrity": "sha512-HogauPvgDQHw2xxqKBaFgKTRRcc1xWeI/PByDCf3U6YsaqpF53Mz2CJh8X2bg2bY1RGKb67MZw7DBGFRvXx4bg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -911,16 +875,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.13", - "@angular/core": "18.2.13", - "@angular/platform-browser": "18.2.13", + "@angular/common": "19.0.6", + "@angular/core": "19.0.6", + "@angular/platform-browser": "19.0.6", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-18.2.13.tgz", - "integrity": "sha512-4E4VJDrbOAxS69F9C1twQPbR9AjY47Qlz8+lwg5lJOyUJ4GoEThLbXKfadt/vIeYBwMJ7fIsYWXD0Dlmxh4k+w==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-19.0.6.tgz", + "integrity": "sha512-k8gPWVgJI5tM77Dpehcq0NmJoBpMH2T2RUA7kmrX+gPUjviKZNYvDv0agAyUbvb/MWkjSBD89CJ12e5523WWpw==", "dev": true, "license": "MIT", "engines": { @@ -928,14 +892,14 @@ } }, "node_modules/@angular/localize": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.13.tgz", - "integrity": "sha512-qQaIYdDS/l1w6tr/wpOoimjpmoJU0WmB8AGbNeKLoM36K+ix6hkvn67+UgkpZtaDHZylm8GsGW1NjzpM2tr3pA==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-19.0.6.tgz", + "integrity": "sha512-U3qP/m8/f7hbDG9RlaLHKnuE7jJZm/nvW/wobpN7/eXTfZ9HVyIuBmfA1SEToDcXYh75GorbguzFbqWKFA0b8g==", "license": "MIT", "dependencies": { - "@babel/core": "7.25.2", + "@babel/core": "7.26.0", "@types/babel__core": "7.20.5", - "fast-glob": "3.3.2", + "fast-glob": "3.3.3", "yargs": "^17.2.1" }, "bin": { @@ -947,32 +911,60 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.13", - "@angular/compiler-cli": "18.2.13" + "@angular/compiler": "19.0.6", + "@angular/compiler-cli": "19.0.6" + } + }, + "node_modules/@angular/localize/node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@angular/localize/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/@angular/material": { - "version": "18.2.14", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.14.tgz", - "integrity": "sha512-28pxzJP49Mymt664WnCtPkKeg7kXUsQKTKGf/Kl95rNTEdTJLbnlcc8wV0rT0yQNR7kXgpfBnG7h0ETLv/iu5Q==", + "version": "19.0.5", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-19.0.5.tgz", + "integrity": "sha512-yiW/ZJNkOPlQdqgj5U8DHTu3r7OHMI5R1cAbCpOmHlsVHEoc/Vw4V3RFUgpWLqCGgdRIkayoilMAooT52gG2Dg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/animations": "^18.0.0 || ^19.0.0", - "@angular/cdk": "18.2.14", - "@angular/common": "^18.0.0 || ^19.0.0", - "@angular/core": "^18.0.0 || ^19.0.0", - "@angular/forms": "^18.0.0 || ^19.0.0", - "@angular/platform-browser": "^18.0.0 || ^19.0.0", + "@angular/animations": "^19.0.0 || ^20.0.0", + "@angular/cdk": "19.0.5", + "@angular/common": "^19.0.0 || ^20.0.0", + "@angular/core": "^19.0.0 || ^20.0.0", + "@angular/forms": "^19.0.0 || ^20.0.0", + "@angular/platform-browser": "^19.0.0 || ^20.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/platform-browser": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.13.tgz", - "integrity": "sha512-tu7ZzY6qD3ATdWFzcTcsAKe7M6cJeWbT/4/bF9unyGO3XBPcNYDKoiz10+7ap2PUd0fmPwvuvTvSNJiFEBnB8Q==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.0.6.tgz", + "integrity": "sha512-MWiToGy7Pa0rR61sgnEuu7dfZXpAw0g7nkSnw4xdjUf974OOOfI1LS9O9YevJibtdW8sPa1HaoXXwcb7N03B5A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -981,9 +973,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "18.2.13", - "@angular/common": "18.2.13", - "@angular/core": "18.2.13" + "@angular/animations": "19.0.6", + "@angular/common": "19.0.6", + "@angular/core": "19.0.6" }, "peerDependenciesMeta": { "@angular/animations": { @@ -992,9 +984,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.13.tgz", - "integrity": "sha512-kbQCf9+8EpuJC7buBxhSiwBtXvjAwAKh6MznD6zd2pyCYqfY6gfRCZQRtK59IfgVtKmEONWI9grEyNIRoTmqJg==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-19.0.6.tgz", + "integrity": "sha512-5TGLOwPlLHXJ1+Hs9b3dEmGdTpb7dfLYalVmiMUZOFBry1sMaRuw+nyqjmWn1GP3yD156hzt5QDzWA8A134AfQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1003,16 +995,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.13", - "@angular/compiler": "18.2.13", - "@angular/core": "18.2.13", - "@angular/platform-browser": "18.2.13" + "@angular/common": "19.0.6", + "@angular/compiler": "19.0.6", + "@angular/core": "19.0.6", + "@angular/platform-browser": "19.0.6" } }, "node_modules/@angular/router": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.13.tgz", - "integrity": "sha512-VKmfgi/r/CkyBq9nChQ/ptmfu0JT/8ONnLVJ5H+SkFLRYJcIRyHLKjRihMCyVm6xM5yktOdCaW73NTQrFz7+bg==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-19.0.6.tgz", + "integrity": "sha512-G1oz+TclPk48h6b6B4s5J3DfrDVJrrxKOA+KWeVQP4e1B8ld7/dCMf5nn3yqS4BGs4yLecxMxyvbOvOiZ//lxw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1021,16 +1013,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.13", - "@angular/core": "18.2.13", - "@angular/platform-browser": "18.2.13", + "@angular/common": "19.0.6", + "@angular/core": "19.0.6", + "@angular/platform-browser": "19.0.6", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/service-worker": { - "version": "18.2.13", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-18.2.13.tgz", - "integrity": "sha512-fVC943qEqGNUy923NMmSSzfoIqNw2k2UbG/3Y4QEmel/nZFWHA3PhiYr+lE7J3RhRHFMmnNP1bmXDJgy+R+pzA==", + "version": "19.0.6", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-19.0.6.tgz", + "integrity": "sha512-5uRkHgyI3LuzTxtAw8nZGoV6owykYQoMHc04WhBUtPdQmjImG6w4B5xAiCPvxGn+ZJ8fpOe/GNpkLeSeOQWumg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -1042,8 +1034,32 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.13", - "@angular/core": "18.2.13" + "@angular/common": "19.0.6", + "@angular/core": "19.0.6" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.2.tgz", + "integrity": "sha512-RtWv9jFN2/bLExuZgFFZ0I3pWWeezAHGgrmjqGGWclATl1aDe3yhCUaI0Ilkp6OCk9zX7+FjvDasEX8Q9Rxc5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.1", + "@csstools/css-color-parser": "^3.0.7", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^11.0.2" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", + "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" } }, "node_modules/@babel/code-frame": { @@ -1070,21 +1086,21 @@ } }, "node_modules/@babel/core": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", - "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-module-transforms": "^7.25.2", - "@babel/helpers": "^7.25.0", - "@babel/parser": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.2", - "@babel/types": "^7.25.2", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -1106,27 +1122,28 @@ "license": "MIT" }, "node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", + "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.0", + "@babel/parser": "^7.26.2", + "@babel/types": "^7.26.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", + "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1154,6 +1171,7 @@ "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-member-expression-to-functions": "^7.25.9", @@ -1170,25 +1188,13 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-create-regexp-features-plugin": { "version": "7.26.3", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.26.3.tgz", "integrity": "sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "regexpu-core": "^6.2.0", @@ -1201,25 +1207,13 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-define-polyfill-provider": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -1237,6 +1231,7 @@ "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -1281,6 +1276,7 @@ "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/types": "^7.25.9" }, @@ -1303,6 +1299,7 @@ "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-wrap-function": "^7.25.9", @@ -1315,25 +1312,13 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-replace-supers": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-member-expression-to-functions": "^7.25.9", "@babel/helper-optimise-call-expression": "^7.25.9", @@ -1352,6 +1337,7 @@ "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -1406,6 +1392,7 @@ "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/template": "^7.25.9", "@babel/traverse": "^7.25.9", @@ -1449,6 +1436,7 @@ "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" @@ -1466,6 +1454,7 @@ "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1482,6 +1471,7 @@ "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1498,6 +1488,7 @@ "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", @@ -1516,6 +1507,7 @@ "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/traverse": "^7.25.9" @@ -1533,6 +1525,7 @@ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.9.0" }, @@ -1595,38 +1588,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1638,13 +1606,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", - "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", + "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1826,6 +1794,7 @@ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -1843,6 +1812,7 @@ "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1854,16 +1824,16 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", - "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", + "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-remap-async-to-generator": "^7.25.0", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/traverse": "^7.25.0" + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9", + "@babel/traverse": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1873,15 +1843,16 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", - "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", + "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7" + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-remap-async-to-generator": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1896,6 +1867,7 @@ "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1912,6 +1884,7 @@ "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -1928,6 +1901,7 @@ "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1945,6 +1919,7 @@ "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -1962,6 +1937,7 @@ "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-compilation-targets": "^7.25.9", @@ -1977,25 +1953,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/template": "^7.25.9" @@ -2013,6 +1977,7 @@ "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2029,6 +1994,7 @@ "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -2046,6 +2012,7 @@ "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2062,6 +2029,7 @@ "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -2079,6 +2047,7 @@ "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2095,6 +2064,7 @@ "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2111,6 +2081,7 @@ "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2127,6 +2098,7 @@ "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -2144,6 +2116,7 @@ "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -2162,6 +2135,7 @@ "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2178,6 +2152,7 @@ "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2194,6 +2169,7 @@ "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2210,6 +2186,7 @@ "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2226,6 +2203,7 @@ "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -2243,6 +2221,7 @@ "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.26.0", "@babel/helper-plugin-utils": "^7.25.9" @@ -2260,6 +2239,7 @@ "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -2279,6 +2259,7 @@ "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-module-transforms": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -2296,6 +2277,7 @@ "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -2313,6 +2295,7 @@ "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2329,6 +2312,7 @@ "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2345,6 +2329,7 @@ "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2361,6 +2346,7 @@ "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-compilation-targets": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9", @@ -2379,6 +2365,7 @@ "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-replace-supers": "^7.25.9" @@ -2396,6 +2383,7 @@ "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2412,6 +2400,7 @@ "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -2429,6 +2418,7 @@ "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2445,6 +2435,7 @@ "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-class-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -2462,6 +2453,7 @@ "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-create-class-features-plugin": "^7.25.9", @@ -2474,25 +2466,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-property-literals": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2509,6 +2489,7 @@ "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "regenerator-transform": "^0.15.2" @@ -2520,12 +2501,31 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", + "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-reserved-words": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2537,16 +2537,17 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", - "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.25.9.tgz", + "integrity": "sha512-nZp7GlEl+yULJrClz0SwHPqir3lc0zsPrDHQUcxGspSL7AKrexNSEfTbfqnDNJUO13bgKyfuOLMF8Xqtu8j3YQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, @@ -2563,6 +2564,7 @@ "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2579,6 +2581,7 @@ "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" @@ -2596,6 +2599,7 @@ "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2612,6 +2616,7 @@ "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2628,6 +2633,7 @@ "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2644,6 +2650,7 @@ "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -2660,6 +2667,7 @@ "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -2677,6 +2685,7 @@ "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -2694,6 +2703,7 @@ "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" @@ -2706,94 +2716,81 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", - "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", + "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@babel/compat-data": "^7.25.2", - "@babel/helper-compilation-targets": "^7.25.2", - "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", + "@babel/compat-data": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-validator-option": "^7.25.9", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.7", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-import-assertions": "^7.26.0", + "@babel/plugin-syntax-import-attributes": "^7.26.0", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.25.0", - "@babel/plugin-transform-async-to-generator": "^7.24.7", - "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.25.0", - "@babel/plugin-transform-class-properties": "^7.24.7", - "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.25.0", - "@babel/plugin-transform-computed-properties": "^7.24.7", - "@babel/plugin-transform-destructuring": "^7.24.8", - "@babel/plugin-transform-dotall-regex": "^7.24.7", - "@babel/plugin-transform-duplicate-keys": "^7.24.7", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", - "@babel/plugin-transform-dynamic-import": "^7.24.7", - "@babel/plugin-transform-exponentiation-operator": "^7.24.7", - "@babel/plugin-transform-export-namespace-from": "^7.24.7", - "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.25.1", - "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.25.2", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", - "@babel/plugin-transform-member-expression-literals": "^7.24.7", - "@babel/plugin-transform-modules-amd": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-modules-systemjs": "^7.25.0", - "@babel/plugin-transform-modules-umd": "^7.24.7", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", - "@babel/plugin-transform-new-target": "^7.24.7", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", - "@babel/plugin-transform-numeric-separator": "^7.24.7", - "@babel/plugin-transform-object-rest-spread": "^7.24.7", - "@babel/plugin-transform-object-super": "^7.24.7", - "@babel/plugin-transform-optional-catch-binding": "^7.24.7", - "@babel/plugin-transform-optional-chaining": "^7.24.8", - "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", - "@babel/plugin-transform-private-property-in-object": "^7.24.7", - "@babel/plugin-transform-property-literals": "^7.24.7", - "@babel/plugin-transform-regenerator": "^7.24.7", - "@babel/plugin-transform-reserved-words": "^7.24.7", - "@babel/plugin-transform-shorthand-properties": "^7.24.7", - "@babel/plugin-transform-spread": "^7.24.7", - "@babel/plugin-transform-sticky-regex": "^7.24.7", - "@babel/plugin-transform-template-literals": "^7.24.7", - "@babel/plugin-transform-typeof-symbol": "^7.24.8", - "@babel/plugin-transform-unicode-escapes": "^7.24.7", - "@babel/plugin-transform-unicode-property-regex": "^7.24.7", - "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/plugin-transform-arrow-functions": "^7.25.9", + "@babel/plugin-transform-async-generator-functions": "^7.25.9", + "@babel/plugin-transform-async-to-generator": "^7.25.9", + "@babel/plugin-transform-block-scoped-functions": "^7.25.9", + "@babel/plugin-transform-block-scoping": "^7.25.9", + "@babel/plugin-transform-class-properties": "^7.25.9", + "@babel/plugin-transform-class-static-block": "^7.26.0", + "@babel/plugin-transform-classes": "^7.25.9", + "@babel/plugin-transform-computed-properties": "^7.25.9", + "@babel/plugin-transform-destructuring": "^7.25.9", + "@babel/plugin-transform-dotall-regex": "^7.25.9", + "@babel/plugin-transform-duplicate-keys": "^7.25.9", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-dynamic-import": "^7.25.9", + "@babel/plugin-transform-exponentiation-operator": "^7.25.9", + "@babel/plugin-transform-export-namespace-from": "^7.25.9", + "@babel/plugin-transform-for-of": "^7.25.9", + "@babel/plugin-transform-function-name": "^7.25.9", + "@babel/plugin-transform-json-strings": "^7.25.9", + "@babel/plugin-transform-literals": "^7.25.9", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", + "@babel/plugin-transform-member-expression-literals": "^7.25.9", + "@babel/plugin-transform-modules-amd": "^7.25.9", + "@babel/plugin-transform-modules-commonjs": "^7.25.9", + "@babel/plugin-transform-modules-systemjs": "^7.25.9", + "@babel/plugin-transform-modules-umd": "^7.25.9", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", + "@babel/plugin-transform-new-target": "^7.25.9", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9", + "@babel/plugin-transform-numeric-separator": "^7.25.9", + "@babel/plugin-transform-object-rest-spread": "^7.25.9", + "@babel/plugin-transform-object-super": "^7.25.9", + "@babel/plugin-transform-optional-catch-binding": "^7.25.9", + "@babel/plugin-transform-optional-chaining": "^7.25.9", + "@babel/plugin-transform-parameters": "^7.25.9", + "@babel/plugin-transform-private-methods": "^7.25.9", + "@babel/plugin-transform-private-property-in-object": "^7.25.9", + "@babel/plugin-transform-property-literals": "^7.25.9", + "@babel/plugin-transform-regenerator": "^7.25.9", + "@babel/plugin-transform-regexp-modifiers": "^7.26.0", + "@babel/plugin-transform-reserved-words": "^7.25.9", + "@babel/plugin-transform-shorthand-properties": "^7.25.9", + "@babel/plugin-transform-spread": "^7.25.9", + "@babel/plugin-transform-sticky-regex": "^7.25.9", + "@babel/plugin-transform-template-literals": "^7.25.9", + "@babel/plugin-transform-typeof-symbol": "^7.25.9", + "@babel/plugin-transform-unicode-escapes": "^7.25.9", + "@babel/plugin-transform-unicode-property-regex": "^7.25.9", + "@babel/plugin-transform-unicode-regex": "^7.25.9", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.37.1", + "core-js-compat": "^3.38.1", "semver": "^6.3.1" }, "engines": { @@ -2809,6 +2806,7 @@ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -2819,9 +2817,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", - "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", + "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -2878,18 +2876,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@babel/types": { "version": "7.26.3", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", @@ -2934,6 +2920,121 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz", + "integrity": "sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.1.tgz", + "integrity": "sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.7.tgz", + "integrity": "sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.1", + "@csstools/css-calc": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@ctrl/ngx-emoji-mart": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/@ctrl/ngx-emoji-mart/-/ngx-emoji-mart-9.2.0.tgz", @@ -2947,25 +3048,26 @@ } }, "node_modules/@danielmoncada/angular-datetime-picker": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@danielmoncada/angular-datetime-picker/-/angular-datetime-picker-18.1.0.tgz", - "integrity": "sha512-hNp0DBFlCMIKa6yvZKocmm2ZCVzevX60z/ZIhRlNVaKwU+Wwh9B/KfvqwT03uvmZWSFQSnNUoPGBTyx0fyTayw==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@danielmoncada/angular-datetime-picker/-/angular-datetime-picker-19.0.0.tgz", + "integrity": "sha512-et7y0E7Yn24ey757Txyoq0xokPOzhtqqj23fDvdBmR9oLh4w9A9dQoFA1aizpPw+7kc0dwbid+gGNZxdbrxfng==", "license": "MIT", "dependencies": { "tslib": "^2.6.2" }, "peerDependencies": { - "@angular/cdk": "^18.0.0", - "@angular/common": "^18.0.0", - "@angular/core": "^18.0.0" + "@angular/cdk": "^19.0.0", + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0" } }, "node_modules/@discoveryjs/json-ext": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz", - "integrity": "sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=14.17.0" } @@ -3029,9 +3131,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.0.tgz", + "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==", "cpu": [ "ppc64" ], @@ -3046,9 +3148,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.0.tgz", + "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==", "cpu": [ "arm" ], @@ -3063,9 +3165,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.0.tgz", + "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==", "cpu": [ "arm64" ], @@ -3080,9 +3182,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.0.tgz", + "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==", "cpu": [ "x64" ], @@ -3097,9 +3199,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", "cpu": [ "arm64" ], @@ -3114,9 +3216,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.0.tgz", + "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==", "cpu": [ "x64" ], @@ -3131,9 +3233,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.0.tgz", + "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==", "cpu": [ "arm64" ], @@ -3148,9 +3250,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.0.tgz", + "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==", "cpu": [ "x64" ], @@ -3165,9 +3267,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.0.tgz", + "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==", "cpu": [ "arm" ], @@ -3182,9 +3284,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.0.tgz", + "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==", "cpu": [ "arm64" ], @@ -3199,9 +3301,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.0.tgz", + "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==", "cpu": [ "ia32" ], @@ -3216,9 +3318,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.0.tgz", + "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==", "cpu": [ "loong64" ], @@ -3233,9 +3335,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.0.tgz", + "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==", "cpu": [ "mips64el" ], @@ -3250,9 +3352,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.0.tgz", + "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==", "cpu": [ "ppc64" ], @@ -3267,9 +3369,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.0.tgz", + "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==", "cpu": [ "riscv64" ], @@ -3284,9 +3386,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.0.tgz", + "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==", "cpu": [ "s390x" ], @@ -3301,9 +3403,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", - "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.0.tgz", + "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==", "cpu": [ "x64" ], @@ -3318,9 +3420,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.0.tgz", + "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==", "cpu": [ "x64" ], @@ -3335,9 +3437,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.0.tgz", + "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==", "cpu": [ "arm64" ], @@ -3352,9 +3454,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.0.tgz", + "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==", "cpu": [ "x64" ], @@ -3369,9 +3471,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.0.tgz", + "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==", "cpu": [ "x64" ], @@ -3386,9 +3488,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.0.tgz", + "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==", "cpu": [ "arm64" ], @@ -3403,9 +3505,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.0.tgz", + "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==", "cpu": [ "ia32" ], @@ -3420,9 +3522,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.0.tgz", + "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==", "cpu": [ "x64" ], @@ -3505,9 +3607,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", + "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -3613,9 +3715,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", + "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", "dev": true, "license": "MIT", "engines": { @@ -3633,12 +3735,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", + "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.10.0", "levn": "^0.4.1" }, "engines": { @@ -3655,16 +3758,16 @@ } }, "node_modules/@fortawesome/angular-fontawesome": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.15.0.tgz", - "integrity": "sha512-oxmJDYGNSym5ycFR0LX4ZOPAU+wWmMAznYpkm5DNAtWWkhMLcrZl15eZQmVIEE+qruQ7JiVrg3tpo8bEkFlDgw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-1.0.0.tgz", + "integrity": "sha512-EC2fYuXIuw2ld1kzJi+zysWus6OeGGfLQtbh0hW9zyyq5aBo8ZJkcJKBsVQ8E6Mg7nHyTWaXn+sdcXTPDWz+UQ==", "license": "MIT", "dependencies": { - "@fortawesome/fontawesome-svg-core": "^6.5.2", - "tslib": "^2.6.2" + "@fortawesome/fontawesome-svg-core": "^6.7.1", + "tslib": "^2.8.1" }, "peerDependencies": { - "@angular/core": "^18.0.0" + "@angular/core": "^19.0.0" } }, "node_modules/@fortawesome/fontawesome-common-types": { @@ -3788,51 +3891,54 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.5.0.tgz", - "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.4.tgz", + "integrity": "sha512-fYAKCAcGNMdfjL6hZTRUwkIByQ8EIZCXKrIQZH7XjADnN/xvRUhj8UdBbpC4zoUzvChhkSC/zRKaP/tDs3dZpg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.3", + "@inquirer/core": "^10.1.2", + "@inquirer/figures": "^1.0.9", + "@inquirer/type": "^3.0.2", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/confirm": { - "version": "3.1.22", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.22.tgz", - "integrity": "sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.0.2.tgz", + "integrity": "sha512-KJLUHOaKnNCYzwVbryj3TNBxyZIrr56fR5N45v6K9IPrbT6B7DcudBMfylkV1A8PUdJE15mybkEQyp2/ZUpxUA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.0.10", - "@inquirer/type": "^1.5.2" + "@inquirer/core": "^10.1.0", + "@inquirer/type": "^3.0.1" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/core": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.2.1.tgz", - "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.2.tgz", + "integrity": "sha512-bHd96F3ezHg1mf/J0Rb4CV8ndCN0v28kUlrHqP7+ECm1C/A+paB7Xh2lbMk6x+kweQC+rZOxM/YeKikzxco8bQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.6", - "@inquirer/type": "^2.0.0", - "@types/mute-stream": "^0.0.4", - "@types/node": "^22.5.5", - "@types/wrap-ansi": "^3.0.0", + "@inquirer/figures": "^1.0.9", + "@inquirer/type": "^3.0.2", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", - "mute-stream": "^1.0.0", + "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^6.2.0", @@ -3842,47 +3948,40 @@ "node": ">=18" } }, - "node_modules/@inquirer/core/node_modules/@inquirer/type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-2.0.0.tgz", - "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", - "dev": true, - "license": "MIT", - "dependencies": { - "mute-stream": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@inquirer/editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.2.0.tgz", - "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.1.tgz", + "integrity": "sha512-xn9aDaiP6nFa432i68JCaL302FyL6y/6EG97nAtfIPnWZ+mWPgCMLGc4XZ2QQMsZtu9q3Jd5AzBPjXh10aX9kA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/type": "^1.5.3", + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2", "external-editor": "^3.1.0" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/expand": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.3.0.tgz", - "integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.4.tgz", + "integrity": "sha512-GYocr+BPyxKPxQ4UZyNMqZFSGKScSUc0Vk17II3J+0bDcgGsQm0KYQNooN1Q5iBfXsy3x/VWmHGh20QnzsaHwg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/type": "^1.5.3", + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2", "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/figures": { @@ -3896,129 +3995,150 @@ } }, "node_modules/@inquirer/input": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.3.0.tgz", - "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.1.tgz", + "integrity": "sha512-nAXAHQndZcXB+7CyjIW3XuQZZHbQQ0q8LX6miY6bqAWwDzNa9JUioDBYrFmOUNIsuF08o1WT/m2gbBXvBhYVxg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/type": "^1.5.3" + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/number": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.1.0.tgz", - "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.4.tgz", + "integrity": "sha512-DX7a6IXRPU0j8kr2ovf+QaaDiIf+zEKaZVzCWdLOTk7XigqSXvoh4cul7x68xp54WTQrgSnW7P1WBJDbyY3GhA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/type": "^1.5.3" + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/password": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.2.0.tgz", - "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.4.tgz", + "integrity": "sha512-wiliQOWdjM8FnBmdIHtQV2Ca3S1+tMBUerhyjkRCv1g+4jSvEweGu9GCcvVEgKDhTBT15nrxvk5/bVrGUqSs1w==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/type": "^1.5.3", + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2", "ansi-escapes": "^4.3.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/prompts": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.3.8.tgz", - "integrity": "sha512-b2BudQY/Si4Y2a0PdZZL6BeJtl8llgeZa7U2j47aaJSCeAl1e4UI7y8a9bSkO3o/ZbZrgT5muy/34JbsjfIWxA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.1.0.tgz", + "integrity": "sha512-5U/XiVRH2pp1X6gpNAjWOglMf38/Ys522ncEHIKT1voRUvSj/DQnR22OVxHnwu5S+rCFaUiPQ57JOtMFQayqYA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^2.4.7", - "@inquirer/confirm": "^3.1.22", - "@inquirer/editor": "^2.1.22", - "@inquirer/expand": "^2.1.22", - "@inquirer/input": "^2.2.9", - "@inquirer/number": "^1.0.10", - "@inquirer/password": "^2.1.22", - "@inquirer/rawlist": "^2.2.4", - "@inquirer/search": "^1.0.7", - "@inquirer/select": "^2.4.7" + "@inquirer/checkbox": "^4.0.2", + "@inquirer/confirm": "^5.0.2", + "@inquirer/editor": "^4.1.0", + "@inquirer/expand": "^4.0.2", + "@inquirer/input": "^4.0.2", + "@inquirer/number": "^3.0.2", + "@inquirer/password": "^4.0.2", + "@inquirer/rawlist": "^4.0.2", + "@inquirer/search": "^3.0.2", + "@inquirer/select": "^4.0.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/rawlist": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.3.0.tgz", - "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.4.tgz", + "integrity": "sha512-IsVN2EZdNHsmFdKWx9HaXb8T/s3FlR/U1QPt9dwbSyPtjFbMTlW9CRFvnn0bm/QIsrMRD2oMZqrQpSWPQVbXXg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/type": "^1.5.3", + "@inquirer/core": "^10.1.2", + "@inquirer/type": "^3.0.2", "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/search": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.1.0.tgz", - "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.4.tgz", + "integrity": "sha512-tSkJk2SDmC2MEdTIjknXWmCnmPr5owTs9/xjfa14ol1Oh95n6xW7SYn5fiPk4/vrJPys0ggSWiISdPze4LTa7A==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.3", + "@inquirer/core": "^10.1.2", + "@inquirer/figures": "^1.0.9", + "@inquirer/type": "^3.0.2", "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/select": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.5.0.tgz", - "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.0.4.tgz", + "integrity": "sha512-ZzYLuLoUzTIW9EJm++jBpRiTshGqS3Q1o5qOEQqgzaBlmdsjQr6pA4TUNkwu6OBYgM2mIRbCz6mUhFDfl/GF+w==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^9.1.0", - "@inquirer/figures": "^1.0.5", - "@inquirer/type": "^1.5.3", + "@inquirer/core": "^10.1.2", + "@inquirer/figures": "^1.0.9", + "@inquirer/type": "^3.0.2", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@inquirer/type": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", - "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", + "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", "dev": true, "license": "MIT", - "dependencies": { - "mute-stream": "^1.0.0" - }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" } }, "node_modules/@interactjs/types": { @@ -4130,6 +4250,19 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -4676,6 +4809,7 @@ "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -4703,6 +4837,7 @@ "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=10.0" }, @@ -4720,6 +4855,7 @@ "integrity": "sha512-osjeBqMJ2lb/j/M8NCPjs1ylqWIcTRTycIhVB5pt6LgzgeRSb0YRZ7j9RfA8wIUrsr/medIuhVyonXRZWLyfdw==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@jsonjoy.com/base64": "^1.1.1", "@jsonjoy.com/util": "^1.1.2", @@ -4743,6 +4879,7 @@ "integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=10.0" }, @@ -4759,28 +4896,52 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@listr2/prompt-adapter-inquirer": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.15.tgz", - "integrity": "sha512-MZrGem/Ujjd4cPTLYDfCZK2iKKeiO/8OX13S6jqxldLs0Prf2aGqVlJ77nMBqMv7fzqgXEgjrNHLXcKR8l9lOg==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.18.tgz", + "integrity": "sha512-0hz44rAcrphyXcA8IS7EJ2SCoaBZD2u5goE8S/e+q/DL+dOGpqpcLidVOFeLG3VgML62SXmfRLAhWt0zL1oW4Q==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/type": "^1.5.1" + "@inquirer/type": "^1.5.5" }, "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@inquirer/prompts": ">= 3 < 6" + "@inquirer/prompts": ">= 3 < 8" + } + }, + "node_modules/@listr2/prompt-adapter-inquirer/node_modules/@inquirer/type": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", + "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@listr2/prompt-adapter-inquirer/node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@lmdb/lmdb-darwin-arm64": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.13.tgz", - "integrity": "sha512-uiKPB0Fv6WEEOZjruu9a6wnW/8jrjzlZbxXscMB8kuCJ1k6kHpcBnuvaAWcqhbI7rqX5GKziwWEdD+wi2gNLfA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.1.5.tgz", + "integrity": "sha512-ue5PSOzHMCIYrfvPP/MRS6hsKKLzqqhcdAvJCO8uFlDdj598EhgnacuOTuqA6uBK5rgiZXfDWyb7DVZSiBKxBA==", "cpu": [ "arm64" ], @@ -4792,9 +4953,9 @@ ] }, "node_modules/@lmdb/lmdb-darwin-x64": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.13.tgz", - "integrity": "sha512-bEVIIfK5mSQoG1R19qA+fJOvCB+0wVGGnXHT3smchBVahYBdlPn2OsZZKzlHWfb1E+PhLBmYfqB5zQXFP7hJig==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.1.5.tgz", + "integrity": "sha512-CGhsb0R5vE6mMNCoSfxHFD8QTvBHM51gs4DBeigTYHWnYv2V5YpJkC4rMo5qAAFifuUcc0+a8a3SIU0c9NrfNw==", "cpu": [ "x64" ], @@ -4806,9 +4967,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-arm": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.13.tgz", - "integrity": "sha512-Yml1KlMzOnXj/tnW7yX8U78iAzTk39aILYvCPbqeewAq1kSzl+w59k/fiVkTBfvDi/oW/5YRxL+Fq+Y1Fr1r2Q==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.1.5.tgz", + "integrity": "sha512-3WeW328DN+xB5PZdhSWmqE+t3+44xWXEbqQ+caWJEZfOFdLp9yklBZEbVqVdqzznkoaXJYxTCp996KD6HmANeg==", "cpu": [ "arm" ], @@ -4820,9 +4981,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-arm64": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.13.tgz", - "integrity": "sha512-afbVrsMgZ9dUTNUchFpj5VkmJRxvht/u335jUJ7o23YTbNbnpmXif3VKQGCtnjSh+CZaqm6N3CPG8KO3zwyZ1Q==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.1.5.tgz", + "integrity": "sha512-LAjaoOcBHGj6fiYB8ureiqPoph4eygbXu4vcOF+hsxiY74n8ilA7rJMmGUT0K0JOB5lmRQHSmor3mytRjS4qeQ==", "cpu": [ "arm64" ], @@ -4834,9 +4995,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-x64": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.13.tgz", - "integrity": "sha512-vOtxu0xC0SLdQ2WRXg8Qgd8T32ak4SPqk5zjItRszrJk2BdeXqfGxBJbP7o4aOvSPSmSSv46Lr1EP4HXU8v7Kg==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.1.5.tgz", + "integrity": "sha512-k/IklElP70qdCXOQixclSl2GPLFiopynGoKX1FqDd1/H0E3Fo1oPwjY2rEVu+0nS3AOw1sryStdXk8CW3cVIsw==", "cpu": [ "x64" ], @@ -4848,9 +5009,9 @@ ] }, "node_modules/@lmdb/lmdb-win32-x64": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.13.tgz", - "integrity": "sha512-UCrMJQY/gJnOl3XgbWRZZUvGGBuKy6i0YNSptgMzHBjs+QYDYR1Mt/RLTOPy4fzzves65O1EDmlL//OzEqoLlA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.1.5.tgz", + "integrity": "sha512-KYar6W8nraZfSJspcK7Kp7hdj238X/FNauYbZyrqPBrtsXI1hvI4/KcRcRGP50aQoV7fkKDyJERlrQGMGTZUsA==", "cpu": [ "x64" ], @@ -4931,33 +5092,324 @@ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", "cpu": [ - "x64" + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/canvas": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.65.tgz", + "integrity": "sha512-YcFhXQcp+b2d38zFOJNbpyPHnIL7KAEkhJQ+UeeKI5IpE9B8Cpf/M6RiHPQXSsSqnYbrfFylnW49dyh2oeSblQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@napi-rs/canvas-android-arm64": "0.1.65", + "@napi-rs/canvas-darwin-arm64": "0.1.65", + "@napi-rs/canvas-darwin-x64": "0.1.65", + "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.65", + "@napi-rs/canvas-linux-arm64-gnu": "0.1.65", + "@napi-rs/canvas-linux-arm64-musl": "0.1.65", + "@napi-rs/canvas-linux-riscv64-gnu": "0.1.65", + "@napi-rs/canvas-linux-x64-gnu": "0.1.65", + "@napi-rs/canvas-linux-x64-musl": "0.1.65", + "@napi-rs/canvas-win32-x64-msvc": "0.1.65" + } + }, + "node_modules/@napi-rs/canvas-android-arm64": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.65.tgz", + "integrity": "sha512-ZYwqFYEKcT5Zr8lbiaJNJj/poLaeK2TncolY914r+gD2TJNeP7ZqvE7A2SX/1C9MB4E3DQEwm3YhL3WEf0x3MQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-arm64": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.65.tgz", + "integrity": "sha512-Pg1pfiJEyDIsX+V0QaJPRWvXbw5zmWAk3bivFCvt/5pwZb37/sT6E/RqPHT9NnqpDyKW6SriwY9ypjljysUA1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-darwin-x64": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.65.tgz", + "integrity": "sha512-3Tr+/HjdJN7Z/VKIcsxV2DvDIibZCExgfYTgljCkUSFuoI7iNkOE6Dc1Q6j212EB9PeO8KmfrViBqHYT6IwWkA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.65.tgz", + "integrity": "sha512-3KP+dYObH7CVkZMZWwk1WX9jRjL+EKdQtD43H8MOI+illf+dwqLlecdQ4d9bQRIxELKJ8dyPWY4fOp/Ngufrdg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-gnu": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.65.tgz", + "integrity": "sha512-Ka3StKz7Dq7kjTF3nNJCq43UN/VlANS7qGE3dWkn1d+tQNsCRy/wRmyt1TUFzIjRqcTFMQNRbgYq84+53UBA0A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-arm64-musl": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.65.tgz", + "integrity": "sha512-O4xMASm2JrmqYoiDyxVWi+z5C14H+oVEag2rZ5iIA67dhWqYZB+iO7wCFpBYRj31JPBR29FOsu6X9zL+DwBFdw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.65.tgz", + "integrity": "sha512-dblWDaA59ZU8bPbkfM+riSke7sFbNZ70LEevUdI5rgiFEUzYUQlU34gSBzemTACj5rCWt1BYeu0GfkLSjNMBSw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-gnu": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.65.tgz", + "integrity": "sha512-wsp+atutw13OJXGU3DDkdngtBDoEg01IuK5xMe0L6VFPV8maGkh17CXze078OD5QJOc6kFyw3DDscMLOPF8+oA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-linux-x64-musl": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.65.tgz", + "integrity": "sha512-odX+nN+IozWzhdj31INcHz3Iy9+EckNw+VqsZcaUxZOTu7/3FmktRNI6aC1qe5minZNv1m05YOS1FVf7fvmjlA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/canvas-win32-x64-msvc": { + "version": "0.1.65", + "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.65.tgz", + "integrity": "sha512-RZQX3luWnlNWgdMnLMQ1hyfQraeAn9lnxWWVCHuUM4tAWEV8UDdeb7cMwmJW7eyt8kAosmjeHt3cylQMHOxGFg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.0.1.tgz", + "integrity": "sha512-zM0mVWSXE0a0h9aKACLwKmD6nHcRiKrPpCfvaKqG1CqDEyjEawId0ocXxVzPMCAm6kkWr2P025msfxXEnt8UGQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.0.1", + "@napi-rs/nice-android-arm64": "1.0.1", + "@napi-rs/nice-darwin-arm64": "1.0.1", + "@napi-rs/nice-darwin-x64": "1.0.1", + "@napi-rs/nice-freebsd-x64": "1.0.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.0.1", + "@napi-rs/nice-linux-arm64-gnu": "1.0.1", + "@napi-rs/nice-linux-arm64-musl": "1.0.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.0.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.0.1", + "@napi-rs/nice-linux-s390x-gnu": "1.0.1", + "@napi-rs/nice-linux-x64-gnu": "1.0.1", + "@napi-rs/nice-linux-x64-musl": "1.0.1", + "@napi-rs/nice-win32-arm64-msvc": "1.0.1", + "@napi-rs/nice-win32-ia32-msvc": "1.0.1", + "@napi-rs/nice-win32-x64-msvc": "1.0.1" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.0.1.tgz", + "integrity": "sha512-5qpvOu5IGwDo7MEKVqqyAxF90I6aLj4n07OzpARdgDRfz8UbBztTByBp0RC59r3J1Ij8uzYi6jI7r5Lws7nn6w==", + "cpu": [ + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" - ] + "android" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", - "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.0.1.tgz", + "integrity": "sha512-GqvXL0P8fZ+mQqG1g0o4AO9hJjQaeYG84FRfZaYjyJtZZZcMjXW5TwkL8Y8UApheJgyE13TQ4YNUssQaTgTyvA==", "cpu": [ - "arm" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "android" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", - "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-91k3HEqUl2fsrz/sKkuEkscj6EAj3/eZNCLqzD2AA0TtVbkQi8nqxZCZDMkfklULmxLkMxuUdKe7RvG/T6s2AA==", "cpu": [ "arm64" ], @@ -4965,13 +5417,16 @@ "license": "MIT", "optional": true, "os": [ - "linux" - ] + "darwin" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", - "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.0.1.tgz", + "integrity": "sha512-jXnMleYSIR/+TAN/p5u+NkCA7yidgswx5ftqzXdD5wgy/hNR92oerTXHc0jrlBisbd7DpzoaGY4cFD7Sm5GlgQ==", "cpu": [ "x64" ], @@ -4979,13 +5434,16 @@ "license": "MIT", "optional": true, "os": [ - "linux" - ] + "darwin" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", - "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.0.1.tgz", + "integrity": "sha512-j+iJ/ezONXRQsVIB/FJfwjeQXX7A2tf3gEXs4WUGFrJjpe/z2KB7sOv6zpkm08PofF36C9S7wTNuzHZ/Iiccfw==", "cpu": [ "x64" ], @@ -4993,86 +5451,88 @@ "license": "MIT", "optional": true, "os": [ - "win32" - ] + "freebsd" + ], + "engines": { + "node": ">= 10" + } }, - "node_modules/@napi-rs/canvas": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.65.tgz", - "integrity": "sha512-YcFhXQcp+b2d38zFOJNbpyPHnIL7KAEkhJQ+UeeKI5IpE9B8Cpf/M6RiHPQXSsSqnYbrfFylnW49dyh2oeSblQ==", + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.0.1.tgz", + "integrity": "sha512-G8RgJ8FYXYkkSGQwywAUh84m946UTn6l03/vmEXBYNJxQJcD+I3B3k5jmjFG/OPiU8DfvxutOP8bi+F89MCV7Q==", + "cpu": [ + "arm" + ], + "dev": true, "license": "MIT", "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">= 10" - }, - "optionalDependencies": { - "@napi-rs/canvas-android-arm64": "0.1.65", - "@napi-rs/canvas-darwin-arm64": "0.1.65", - "@napi-rs/canvas-darwin-x64": "0.1.65", - "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.65", - "@napi-rs/canvas-linux-arm64-gnu": "0.1.65", - "@napi-rs/canvas-linux-arm64-musl": "0.1.65", - "@napi-rs/canvas-linux-riscv64-gnu": "0.1.65", - "@napi-rs/canvas-linux-x64-gnu": "0.1.65", - "@napi-rs/canvas-linux-x64-musl": "0.1.65", - "@napi-rs/canvas-win32-x64-msvc": "0.1.65" } }, - "node_modules/@napi-rs/canvas-android-arm64": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.65.tgz", - "integrity": "sha512-ZYwqFYEKcT5Zr8lbiaJNJj/poLaeK2TncolY914r+gD2TJNeP7ZqvE7A2SX/1C9MB4E3DQEwm3YhL3WEf0x3MQ==", + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.0.1.tgz", + "integrity": "sha512-IMDak59/W5JSab1oZvmNbrms3mHqcreaCeClUjwlwDr0m3BoR09ZiN8cKFBzuSlXgRdZ4PNqCYNeGQv7YMTjuA==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "android" + "linux" ], "engines": { "node": ">= 10" } }, - "node_modules/@napi-rs/canvas-darwin-arm64": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.65.tgz", - "integrity": "sha512-Pg1pfiJEyDIsX+V0QaJPRWvXbw5zmWAk3bivFCvt/5pwZb37/sT6E/RqPHT9NnqpDyKW6SriwY9ypjljysUA1Q==", + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.0.1.tgz", + "integrity": "sha512-wG8fa2VKuWM4CfjOjjRX9YLIbysSVV1S3Kgm2Fnc67ap/soHBeYZa6AGMeR5BJAylYRjnoVOzV19Cmkco3QEPw==", "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { "node": ">= 10" } }, - "node_modules/@napi-rs/canvas-darwin-x64": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.65.tgz", - "integrity": "sha512-3Tr+/HjdJN7Z/VKIcsxV2DvDIibZCExgfYTgljCkUSFuoI7iNkOE6Dc1Q6j212EB9PeO8KmfrViBqHYT6IwWkA==", + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.0.1.tgz", + "integrity": "sha512-lxQ9WrBf0IlNTCA9oS2jg/iAjQyTI6JHzABV664LLrLA/SIdD+I1i3Mjf7TsnoUbgopBcCuDztVLfJ0q9ubf6Q==", "cpu": [ - "x64" + "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ], "engines": { "node": ">= 10" } }, - "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.65.tgz", - "integrity": "sha512-3KP+dYObH7CVkZMZWwk1WX9jRjL+EKdQtD43H8MOI+illf+dwqLlecdQ4d9bQRIxELKJ8dyPWY4fOp/Ngufrdg==", + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.0.1.tgz", + "integrity": "sha512-3xs69dO8WSWBb13KBVex+yvxmUeEsdWexxibqskzoKaWx9AIqkMbWmE2npkazJoopPKX2ULKd8Fm9veEn0g4Ig==", "cpu": [ - "arm" + "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5082,13 +5542,14 @@ "node": ">= 10" } }, - "node_modules/@napi-rs/canvas-linux-arm64-gnu": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.65.tgz", - "integrity": "sha512-Ka3StKz7Dq7kjTF3nNJCq43UN/VlANS7qGE3dWkn1d+tQNsCRy/wRmyt1TUFzIjRqcTFMQNRbgYq84+53UBA0A==", + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.0.1.tgz", + "integrity": "sha512-lMFI3i9rlW7hgToyAzTaEybQYGbQHDrpRkg+1gJWEpH0PLAQoZ8jiY0IzakLfNWnVda1eTYYlxxFYzW8Rqczkg==", "cpu": [ - "arm64" + "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5098,13 +5559,14 @@ "node": ">= 10" } }, - "node_modules/@napi-rs/canvas-linux-arm64-musl": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.65.tgz", - "integrity": "sha512-O4xMASm2JrmqYoiDyxVWi+z5C14H+oVEag2rZ5iIA67dhWqYZB+iO7wCFpBYRj31JPBR29FOsu6X9zL+DwBFdw==", + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.0.1.tgz", + "integrity": "sha512-XQAJs7DRN2GpLN6Fb+ZdGFeYZDdGl2Fn3TmFlqEL5JorgWKrQGRUrpGKbgZ25UeZPILuTKJ+OowG2avN8mThBA==", "cpu": [ - "arm64" + "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5114,13 +5576,14 @@ "node": ">= 10" } }, - "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.65.tgz", - "integrity": "sha512-dblWDaA59ZU8bPbkfM+riSke7sFbNZ70LEevUdI5rgiFEUzYUQlU34gSBzemTACj5rCWt1BYeu0GfkLSjNMBSw==", + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.0.1.tgz", + "integrity": "sha512-/rodHpRSgiI9o1faq9SZOp/o2QkKQg7T+DK0R5AkbnI/YxvAIEHf2cngjYzLMQSQgUhxym+LFr+UGZx4vK4QdQ==", "cpu": [ - "riscv64" + "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5130,45 +5593,48 @@ "node": ">= 10" } }, - "node_modules/@napi-rs/canvas-linux-x64-gnu": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.65.tgz", - "integrity": "sha512-wsp+atutw13OJXGU3DDkdngtBDoEg01IuK5xMe0L6VFPV8maGkh17CXze078OD5QJOc6kFyw3DDscMLOPF8+oA==", + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.0.1.tgz", + "integrity": "sha512-rEcz9vZymaCB3OqEXoHnp9YViLct8ugF+6uO5McifTedjq4QMQs3DHz35xBEGhH3gJWEsXMUbzazkz5KNM5YUg==", "cpu": [ - "x64" + "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], "engines": { "node": ">= 10" } }, - "node_modules/@napi-rs/canvas-linux-x64-musl": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.65.tgz", - "integrity": "sha512-odX+nN+IozWzhdj31INcHz3Iy9+EckNw+VqsZcaUxZOTu7/3FmktRNI6aC1qe5minZNv1m05YOS1FVf7fvmjlA==", + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.0.1.tgz", + "integrity": "sha512-t7eBAyPUrWL8su3gDxw9xxxqNwZzAqKo0Szv3IjVQd1GpXXVkb6vBBQUuxfIYaXMzZLwlxRQ7uzM2vdUE9ULGw==", "cpu": [ - "x64" + "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ], "engines": { "node": ">= 10" } }, - "node_modules/@napi-rs/canvas-win32-x64-msvc": { - "version": "0.1.65", - "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.65.tgz", - "integrity": "sha512-RZQX3luWnlNWgdMnLMQ1hyfQraeAn9lnxWWVCHuUM4tAWEV8UDdeb7cMwmJW7eyt8kAosmjeHt3cylQMHOxGFg==", + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.0.1.tgz", + "integrity": "sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -5191,36 +5657,37 @@ } }, "node_modules/@ng-bootstrap/ng-bootstrap": { - "version": "17.0.1", - "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-17.0.1.tgz", - "integrity": "sha512-utbm8OXIoqVVYGVzQkOS773ymbjc+UMkXv8lyi7hTqLhCQs0rZ0yA74peqVZRuOGXLHgcSTA7fnJhA80iQOblw==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-18.0.0.tgz", + "integrity": "sha512-GeSAz4yiGq49psdte8kcf+Y562wB3jK/qKRAkh6iA32lcXmy2sfQXVAmlHdjZ3AyP+E8lf3yMwuPdSKiYcDgSg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": "^18.0.0", - "@angular/core": "^18.0.0", - "@angular/forms": "^18.0.0", - "@angular/localize": "^18.0.0", + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/forms": "^19.0.0", + "@angular/localize": "^19.0.0", "@popperjs/core": "^2.11.8", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@ngtools/webpack": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.12.tgz", - "integrity": "sha512-FFJAwtWbtpncMOVNuULPBwFJB7GSjiUwO93eGTzRp8O4EPQ8lCQeFbezQm/NP34+T0+GBLGzPSuQT+muob8YKw==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-19.0.7.tgz", + "integrity": "sha512-jWyMuqtLKZB8Jnuqo27mG2cCQdl71lhM1oEdq3x7Z/QOrm2I+8EfyAzOLxB1f1vXt85O1bz3nf66CkuVCVGGTQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "peerDependencies": { - "@angular/compiler-cli": "^18.0.0", - "typescript": ">=5.4 <5.6", + "@angular/compiler-cli": "^19.0.0", + "typescript": ">=5.5 <5.7", "webpack": "^5.54.0" } }, @@ -5286,9 +5753,9 @@ } }, "node_modules/@npmcli/agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", - "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", + "integrity": "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==", "dev": true, "license": "ISC", "dependencies": { @@ -5299,7 +5766,7 @@ "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/agent/node_modules/lru-cache": { @@ -5310,37 +5777,37 @@ "license": "ISC" }, "node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz", + "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==", "dev": true, "license": "ISC", "dependencies": { "semver": "^7.3.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/git": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", - "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-6.0.1.tgz", + "integrity": "sha512-BBWMMxeQzalmKadyimwb2/VVQyJB01PH0HhVSNLHNBDZN/M/h/02P6f8fxedIiFhpMj11SO9Ep5tKTBE7zL2nw==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/promise-spawn": "^7.0.0", - "ini": "^4.1.3", + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", "lru-cache": "^10.0.1", - "npm-pick-manifest": "^9.0.0", - "proc-log": "^4.0.0", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", - "which": "^4.0.0" + "which": "^5.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/git/node_modules/isexe": { @@ -5361,9 +5828,9 @@ "license": "ISC" }, "node_modules/@npmcli/git/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, "license": "ISC", "dependencies": { @@ -5373,53 +5840,53 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/installed-package-contents": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", - "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-3.0.0.tgz", + "integrity": "sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==", "dev": true, "license": "ISC", "dependencies": { - "npm-bundled": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" }, "bin": { "installed-package-contents": "bin/index.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/node-gyp": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", - "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-4.0.0.tgz", + "integrity": "sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/package-json": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.1.tgz", - "integrity": "sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-6.1.0.tgz", + "integrity": "sha512-t6G+6ZInT4X+tqj2i+wlLIeCKnKOTuz9/VFYDtj+TGTur5q7sp/OYrQA19LdBbWfXDOi0Y4jtedV6xtB8zQ9ug==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^5.0.0", + "@npmcli/git": "^6.0.0", "glob": "^10.2.2", - "hosted-git-info": "^7.0.0", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^6.0.0", - "proc-log": "^4.0.0", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "normalize-package-data": "^7.0.0", + "proc-log": "^5.0.0", "semver": "^7.5.3" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/package-json/node_modules/glob": { @@ -5444,16 +5911,16 @@ } }, "node_modules/@npmcli/promise-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", - "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.2.tgz", + "integrity": "sha512-/bNJhjc+o6qL+Dwz/bqfTQClkEO5nTQ1ZEcdCkAQjhkZMHIh22LPG7fNh1enJP1NKWDqYiiABnjFCY7E0zHYtQ==", "dev": true, "license": "ISC", "dependencies": { - "which": "^4.0.0" + "which": "^5.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/promise-spawn/node_modules/isexe": { @@ -5467,9 +5934,9 @@ } }, "node_modules/@npmcli/promise-spawn/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, "license": "ISC", "dependencies": { @@ -5479,35 +5946,35 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/redact": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", - "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-3.0.0.tgz", + "integrity": "sha512-/1uFzjVcfzqrgCeGW7+SZ4hv0qLWmKXVzFahZGJ6QuJBj6Myt9s17+JL86i76NV9YSnJRcGXJYQbAU0rn1YTCQ==", "dev": true, "license": "ISC", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/run-script": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", - "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-9.0.2.tgz", + "integrity": "sha512-cJXiUlycdizQwvqE1iaAb4VRUM3RX09/8q46zjvy+ct9GhfZRWd7jXYVc1tn/CfRlGPVkX/u4sstRlepsm7hfw==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^5.0.0", - "@npmcli/promise-spawn": "^7.0.0", - "node-gyp": "^10.0.0", - "proc-log": "^4.0.0", - "which": "^4.0.0" + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@npmcli/run-script/node_modules/isexe": { @@ -5521,9 +5988,9 @@ } }, "node_modules/@npmcli/run-script/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, "license": "ISC", "dependencies": { @@ -5533,13 +6000,13 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@nx/devkit": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-20.3.0.tgz", - "integrity": "sha512-u9oRd2F33DLNWPbzpYGW7xuMEYUAOwO9DLP9nGYpxbZXy6Z4AdoKeqhN+KBTyg8+DyQGuKUSEXcWriDyLLgcHw==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-20.3.1.tgz", + "integrity": "sha512-Z6VdBg5GRu2Vg9FpeQJY+zQ1TvBoMWk8cTCZOf8J6myjoWYbksRfpWfNIvEk9OUsEMhpg98vxH2Cc8JR1zfiew==", "dev": true, "license": "MIT", "dependencies": { @@ -5593,9 +6060,9 @@ } }, "node_modules/@nx/nx-darwin-arm64": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.3.0.tgz", - "integrity": "sha512-9PqSe1Sh7qNqA4GL0cZH0t3S0EZzb2Xn14XY9au7yf0+eoxyag1oETjjULrxLeUmSoXW2hDxzNtoqKFE9zF07Q==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-20.3.1.tgz", + "integrity": "sha512-bx++T9/8l4PK1yDTxPnROT7RG8CkWGkxKC0D7xlS/YQzE7CelDfgNYu0Bd7upZF4gafW2Uz3dd3j6WhvZLxbbg==", "cpu": [ "arm64" ], @@ -5610,9 +6077,9 @@ } }, "node_modules/@nx/nx-darwin-x64": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.3.0.tgz", - "integrity": "sha512-gsGGhJVvi5QZVVTZie5sNMo1zOAU+A2edm6DGegObdFRLV41Ju/Yrm/gTaSp4yUtywd3UU4S/30C/nI2c55adA==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-20.3.1.tgz", + "integrity": "sha512-elg2GiSivMHU1iLFYZ+FojM2V/FmTlC8e5FKM6nZ+bIqeoBoJm8Rxxe/kEtcsPdvjj+YiKSmXOP9s45DJb9WWw==", "cpu": [ "x64" ], @@ -5627,9 +6094,9 @@ } }, "node_modules/@nx/nx-freebsd-x64": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.3.0.tgz", - "integrity": "sha512-DiymYZBBu0upbiskdfn9KRyoXdyvKohezJiV3j4VkeRE8KR2p04NgwRQviDFbeD1cjWrDy9wk8y+G5PabLlqAA==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-20.3.1.tgz", + "integrity": "sha512-1iKZOCcU7bVAC2kdoukfJ7AOTLBhm69+vPff3HCJQ0DI/5ZbmiaPeBMsAVFtJ0jFGix8yYIhgvtXgDEfbXXRFQ==", "cpu": [ "x64" ], @@ -5644,9 +6111,9 @@ } }, "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.3.0.tgz", - "integrity": "sha512-Aksx66e8jmt/4rGJ/5z34SWXbPcYr9Ht52UonEeuCdQdoEvAOs7yBUbllYOjIcUsfZikEyZgvqfiQslsggSJdQ==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-20.3.1.tgz", + "integrity": "sha512-LAteJ1/mWYdvj7zpXuWRUq1lvUiV6YVXCdFK3+7lDW+qvW3bb5zzUwbVDAF/pPeTjBrsdHDzSWOCLm/LKtYtMw==", "cpu": [ "arm" ], @@ -5661,9 +6128,9 @@ } }, "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.3.0.tgz", - "integrity": "sha512-Y5wmYEwF1bl014Ps8QjagI911VbViQSFHSTVOCNSObdAzig9E5o6NOkoWe+doT1UZLrrInnlkrggQUsbtdKjOg==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-20.3.1.tgz", + "integrity": "sha512-2Qf+6NcAeODELyJR+V9hjC9kl2DwJTdI7Bw+BuiyXftfPHvZ86P//FC8kPjNaJCEEm/ZStP6Jcb1zlp4Eo2wBw==", "cpu": [ "arm64" ], @@ -5678,9 +6145,9 @@ } }, "node_modules/@nx/nx-linux-arm64-musl": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.3.0.tgz", - "integrity": "sha512-yGcIkmImyOMfPkQSYH2EVjPmFE0VkLcO71Bbkpr3RlJ1N/vjYxsGbdnqPiBb8Wshib/hmwpiMHf/yzQtKH0SQw==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-20.3.1.tgz", + "integrity": "sha512-8S8jlN6GFQpRakZ2ZVWq6eFnLVrEObIaxnYD0QMbsMf+qiedDJt+cDh1xebcPRvgpSgJVlJ8P6hun5+K/FiQDQ==", "cpu": [ "arm64" ], @@ -5695,9 +6162,9 @@ } }, "node_modules/@nx/nx-linux-x64-gnu": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.3.0.tgz", - "integrity": "sha512-nkA2DLI+rpmiuiy7dyXP4l9s7dgHkQWDX7lG1XltiT41RzAReJF1h8qBE6XrsAYE1CtI76DRWVphnc93+iZr+A==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-20.3.1.tgz", + "integrity": "sha512-qC2On2qwYCtn/Kt8epvUn0H3NY6zG9yYhiNjkm6RvVTDmvogFQ4gtfiWSRP/EnabCRqM8FACDIO/ws5CnRBX+Q==", "cpu": [ "x64" ], @@ -5712,9 +6179,9 @@ } }, "node_modules/@nx/nx-linux-x64-musl": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.3.0.tgz", - "integrity": "sha512-sPMtTt9iTrCmFEIp9Qv27UX9PeL1aqKck2dz2TAFbXKVtF6+djOdTcNnTYw45KIP6izcUcOXXAq4G0QSQE7CLg==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-20.3.1.tgz", + "integrity": "sha512-KKwHSfV1PEKW82eJ8vxZTPepoaLbaXH/aI0VOKZbBO4ytGyGUr9wFuWPsyo06rK7qtSD7w9bN7xpiBGQk0QTsg==", "cpu": [ "x64" ], @@ -5729,9 +6196,9 @@ } }, "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.3.0.tgz", - "integrity": "sha512-ppfNa/8OfpWA9o26Pz3vArN4ulAC+Hx70/ghPRCP7ed1Mb3Z6yR2Ry9KfBRImbqajvuAExM0TePKMGq9LCdXmg==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-20.3.1.tgz", + "integrity": "sha512-YujkXXHn9rhtwZRDxiaxSPOMX7JkfGmXAFdyEfxhE3Dc/HjFgI+xJZ478/atttR7DWIwGpQJVLpbFWbFFpoNNg==", "cpu": [ "arm64" ], @@ -5746,9 +6213,9 @@ } }, "node_modules/@nx/nx-win32-x64-msvc": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.3.0.tgz", - "integrity": "sha512-8FOejZ4emtLSVn3pYWs4PIc3n4//qMbwMDPVxmPE8us3ir91Qh0bzr5zRj7Q8sEdSgvneXRXqtBp2grY2KMJsw==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-20.3.1.tgz", + "integrity": "sha512-Os8iCamvHhE5noQKFE9D9xkiI529918tufTYmEhJ9ZmLU/ybVA0We6r7gXjYzdNfA3DtwfGXvNvUpy3u+pZXOg==", "cpu": [ "x64" ], @@ -6204,9 +6671,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", - "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.26.0.tgz", + "integrity": "sha512-gJNwtPDGEaOEgejbaseY6xMFu+CPltsc8/T+diUTTbOQLqD+bnrJq9ulH6WD69TqwqWmrfRAtUv30cCFZlbGTQ==", "cpu": [ "arm" ], @@ -6218,9 +6685,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", - "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.26.0.tgz", + "integrity": "sha512-YJa5Gy8mEZgz5JquFruhJODMq3lTHWLm1fOy+HIANquLzfIOzE9RA5ie3JjCdVb9r46qfAQY/l947V0zfGJ0OQ==", "cpu": [ "arm64" ], @@ -6232,9 +6699,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", - "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.26.0.tgz", + "integrity": "sha512-ErTASs8YKbqTBoPLp/kA1B1Um5YSom8QAc4rKhg7b9tyyVqDBlQxy7Bf2wW7yIlPGPg2UODDQcbkTlruPzDosw==", "cpu": [ "arm64" ], @@ -6246,9 +6713,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", - "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.26.0.tgz", + "integrity": "sha512-wbgkYDHcdWW+NqP2mnf2NOuEbOLzDblalrOWcPyY6+BRbVhliavon15UploG7PpBRQ2bZJnbmh8o3yLoBvDIHA==", "cpu": [ "x64" ], @@ -6259,10 +6726,38 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.26.0.tgz", + "integrity": "sha512-Y9vpjfp9CDkAG4q/uwuhZk96LP11fBz/bYdyg9oaHYhtGZp7NrbkQrj/66DYMMP2Yo/QPAsVHkV891KyO52fhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.26.0.tgz", + "integrity": "sha512-A/jvfCZ55EYPsqeaAt/yDAG4q5tt1ZboWMHEvKAH9Zl92DWvMIbnZe/f/eOXze65aJaaKbL+YeM0Hz4kLQvdwg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", - "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.26.0.tgz", + "integrity": "sha512-paHF1bMXKDuizaMODm2bBTjRiHxESWiIyIdMugKeLnjuS1TCS54MF5+Y5Dx8Ui/1RBPVRE09i5OUlaLnv8OGnA==", "cpu": [ "arm" ], @@ -6274,9 +6769,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", - "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.26.0.tgz", + "integrity": "sha512-cwxiHZU1GAs+TMxvgPfUDtVZjdBdTsQwVnNlzRXC5QzIJ6nhfB4I1ahKoe9yPmoaA/Vhf7m9dB1chGPpDRdGXg==", "cpu": [ "arm" ], @@ -6288,9 +6783,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", - "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.26.0.tgz", + "integrity": "sha512-4daeEUQutGRCW/9zEo8JtdAgtJ1q2g5oHaoQaZbMSKaIWKDQwQ3Yx0/3jJNmpzrsScIPtx/V+1AfibLisb3AMQ==", "cpu": [ "arm64" ], @@ -6302,9 +6797,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", - "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.26.0.tgz", + "integrity": "sha512-eGkX7zzkNxvvS05ROzJ/cO/AKqNvR/7t1jA3VZDi2vRniLKwAWxUr85fH3NsvtxU5vnUUKFHKh8flIBdlo2b3Q==", "cpu": [ "arm64" ], @@ -6316,9 +6811,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", - "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.26.0.tgz", + "integrity": "sha512-Odp/lgHbW/mAqw/pU21goo5ruWsytP7/HCC/liOt0zcGG0llYWKrd10k9Fj0pdj3prQ63N5yQLCLiE7HTX+MYw==", "cpu": [ "ppc64" ], @@ -6330,9 +6825,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", - "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.26.0.tgz", + "integrity": "sha512-MBR2ZhCTzUgVD0OJdTzNeF4+zsVogIR1U/FsyuFerwcqjZGvg2nYe24SAHp8O5sN8ZkRVbHwlYeHqcSQ8tcYew==", "cpu": [ "riscv64" ], @@ -6344,9 +6839,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", - "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.26.0.tgz", + "integrity": "sha512-YYcg8MkbN17fMbRMZuxwmxWqsmQufh3ZJFxFGoHjrE7bv0X+T6l3glcdzd7IKLiwhT+PZOJCblpnNlz1/C3kGQ==", "cpu": [ "s390x" ], @@ -6358,9 +6853,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", - "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.26.0.tgz", + "integrity": "sha512-ZuwpfjCwjPkAOxpjAEjabg6LRSfL7cAJb6gSQGZYjGhadlzKKywDkCUnJ+KEfrNY1jH5EEoSIKLCb572jSiglA==", "cpu": [ "x64" ], @@ -6372,9 +6867,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", - "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.26.0.tgz", + "integrity": "sha512-+HJD2lFS86qkeF8kNu0kALtifMpPCZU80HvwztIKnYwym3KnA1os6nsX4BGSTLtS2QVAGG1P3guRgsYyMA0Yhg==", "cpu": [ "x64" ], @@ -6386,9 +6881,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", - "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.26.0.tgz", + "integrity": "sha512-WUQzVFWPSw2uJzX4j6YEbMAiLbs0BUysgysh8s817doAYhR5ybqTI1wtKARQKo6cGop3pHnrUJPFCsXdoFaimQ==", "cpu": [ "arm64" ], @@ -6400,9 +6895,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", - "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.26.0.tgz", + "integrity": "sha512-D4CxkazFKBfN1akAIY6ieyOqzoOoBV1OICxgUblWxff/pSjCA2khXlASUx7mK6W1oP4McqhgcCsu6QaLj3WMWg==", "cpu": [ "ia32" ], @@ -6414,9 +6909,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", - "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.26.0.tgz", + "integrity": "sha512-2x8MO1rm4PGEP0xWbubJW5RtbNLk3puzAMaLQd3B3JHVw4KcHlmXcO+Wewx9zCoo7EUFiMlu/aZbCJ7VjMzAag==", "cpu": [ "x64" ], @@ -6428,14 +6923,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.12.tgz", - "integrity": "sha512-sIoeipsisK5eTLW3XuNZYcal83AfslBbgI7LnV+3VrXwpasKPGHwo2ZdwhCd2IXAkuJ02Iyu7MyV0aQRM9i/3g==", + "version": "19.0.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.0.7.tgz", + "integrity": "sha512-1WtTqKFPuEaV99VIP+y/gf/XW3TVJh/NbJbbEF4qYpp7qQiJ4ntF4klVZmsJcQzFucZSzlg91QVMPQKev5WZGA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.12", - "@angular-devkit/schematics": "18.2.12", + "@angular-devkit/core": "19.0.7", + "@angular-devkit/schematics": "19.0.7", "jsonc-parser": "3.3.1" }, "engines": { @@ -6445,63 +6940,63 @@ } }, "node_modules/@sentry-internal/browser-utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.47.0.tgz", - "integrity": "sha512-vOXzYzHTKkahTLDzWWIA4EiVCQ+Gk+7xGWUlNcR2ZiEPBqYZVb5MjsUozAcc7syrSUy6WicyFjcomZ3rlCVQhg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.48.0.tgz", + "integrity": "sha512-pLtu0Fa1Ou0v3M1OEO1MB1EONJVmXEGtoTwFRCO1RPQI2ulmkG6BikINClFG5IBpoYKZ33WkEXuM6U5xh+pdZg==", "license": "MIT", "dependencies": { - "@sentry/core": "8.47.0" + "@sentry/core": "8.48.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/feedback": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.47.0.tgz", - "integrity": "sha512-IAiIemTQIalxAOYhUENs9bZ8pMNgJnX3uQSuY7v0gknEqClOGpGkG04X/cxCmtJUj1acZ9ShTGDxoh55a+ggAQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.48.0.tgz", + "integrity": "sha512-6PwcJNHVPg0EfZxmN+XxVOClfQpv7MBAweV8t9i5l7VFr8sM/7wPNSeU/cG7iK19Ug9ZEkBpzMOe3G4GXJ5bpw==", "license": "MIT", "dependencies": { - "@sentry/core": "8.47.0" + "@sentry/core": "8.48.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.47.0.tgz", - "integrity": "sha512-G/S40ZBORj0HSMLw/uVC6YDEPN/dqVk901vf4VYfml686DEhJrZesfAfp5SydJumQ0NKZQrdtvny+BWnlI5H1w==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.48.0.tgz", + "integrity": "sha512-csILVupc5RkrsTrncuUTGmlB56FQSFjXPYWG8I8yBTGlXEJ+o8oTuF6+55R4vbw3EIzBveXWi4kEBbnQlXW/eg==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.47.0", - "@sentry/core": "8.47.0" + "@sentry-internal/browser-utils": "8.48.0", + "@sentry/core": "8.48.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.47.0.tgz", - "integrity": "sha512-M4W9UGouEeELbGbP3QsXLDVtGiQSZoWJlKwqMWyqdQgZuLoKw0S33+60t6teLVMhuQZR0UI9VJTF5coiXysnnA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.48.0.tgz", + "integrity": "sha512-LdivLfBXXB9us1aAc6XaL7/L2Ob4vi3C/fEOXElehg3qHjX6q6pewiv5wBvVXGX1NfZTRvu+X11k6TZoxKsezw==", "license": "MIT", "dependencies": { - "@sentry-internal/replay": "8.47.0", - "@sentry/core": "8.47.0" + "@sentry-internal/replay": "8.48.0", + "@sentry/core": "8.48.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/angular": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.47.0.tgz", - "integrity": "sha512-SHKyzd8OOrlGcnKItIFYp9wJXIReo1LwNDRQ8zJvUhMwdCHfZW0oZOSXTt8Ol4MfU/lWEgrFg+SyhA3Ltiz2uw==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.48.0.tgz", + "integrity": "sha512-jJRrsm5y72L9TPBIRITOEryiO4MM0XP2ZAKc943v76dk6Q47SdataUdF6Lwf+MxobnE7S+zY97DnsxLGsOxF4w==", "license": "MIT", "dependencies": { - "@sentry/browser": "8.47.0", - "@sentry/core": "8.47.0", + "@sentry/browser": "8.48.0", + "@sentry/core": "8.48.0", "tslib": "^2.4.1" }, "engines": { @@ -6515,38 +7010,38 @@ } }, "node_modules/@sentry/browser": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.47.0.tgz", - "integrity": "sha512-K6BzHisykmbFy/wORtGyfsAlw7ShevLALzu3ReZZZ18dVubO1bjSNjkZQU9MJD5Jcb9oLwkq89n3N9XIBfvdRA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.48.0.tgz", + "integrity": "sha512-fuuVULB5/1vI8NoIwXwR3xwhJJqk+y4RdSdajExGF7nnUDBpwUJyXsmYJnOkBO+oLeEs58xaCpotCKiPUNnE3g==", "license": "MIT", "dependencies": { - "@sentry-internal/browser-utils": "8.47.0", - "@sentry-internal/feedback": "8.47.0", - "@sentry-internal/replay": "8.47.0", - "@sentry-internal/replay-canvas": "8.47.0", - "@sentry/core": "8.47.0" + "@sentry-internal/browser-utils": "8.48.0", + "@sentry-internal/feedback": "8.48.0", + "@sentry-internal/replay": "8.48.0", + "@sentry-internal/replay-canvas": "8.48.0", + "@sentry/core": "8.48.0" }, "engines": { "node": ">=14.18" } }, "node_modules/@sentry/core": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.47.0.tgz", - "integrity": "sha512-iSEJZMe3DOcqBFZQAqgA3NB2lCWBc4Gv5x/SCri/TVg96wAlss4VrUunSI2Mp0J4jJ5nJcJ2ChqHSBAU48k3FA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.48.0.tgz", + "integrity": "sha512-VGwYgTfLpvJ5LRO5A+qWo1gpo6SfqaGXL9TOzVgBucAdpzbrYHpZ87sEarDVq/4275uk1b0S293/mfsskFczyw==", "license": "MIT", "engines": { "node": ">=14.18" } }, "node_modules/@sentry/types": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.47.0.tgz", - "integrity": "sha512-ruiowlIBQUPDwNcO0KTudKP9T2QYF3S2TLhDdoJb+0ZGJduH4PsgGAojUSpGR+idKfrlOSlUpcdg9+WxsoOckg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.48.0.tgz", + "integrity": "sha512-3Ad38BF9b+sVRh7ag8jryHuqVOzQ4mBKjT6vnm+jQz45Qbhri//LSdhhJ7KpiSm3/EYpRjQekTr6mjYZmfOaeA==", "dev": true, "license": "MIT", "dependencies": { - "@sentry/core": "8.47.0" + "@sentry/core": "8.48.0" }, "engines": { "node": ">=14.18" @@ -6568,26 +7063,26 @@ } }, "node_modules/@sigstore/bundle": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", - "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-3.0.0.tgz", + "integrity": "sha512-XDUYX56iMPAn/cdgh/DTJxz5RWmqKV4pwvUAEKEWJl+HzKdCd/24wUa9JYNMlDSCb7SUHAdtksxYX779Nne/Zg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.3.2" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@sigstore/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", - "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-2.0.0.tgz", + "integrity": "sha512-nYxaSb/MtlSI+JWcwTHQxyNmWeWrUXJJ/G4liLrGG7+tS4vAz6LF3xRXqLH6wPIVUoZQel2Fs4ddLx4NCpiIYg==", "dev": true, "license": "Apache-2.0", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@sigstore/protobuf-specs": { @@ -6601,50 +7096,50 @@ } }, "node_modules/@sigstore/sign": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", - "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-3.0.0.tgz", + "integrity": "sha512-UjhDMQOkyDoktpXoc5YPJpJK6IooF2gayAr5LvXI4EL7O0vd58okgfRcxuaH+YTdhvb5aa1Q9f+WJ0c2sVuYIw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.0.0", + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", "@sigstore/protobuf-specs": "^0.3.2", - "make-fetch-happen": "^13.0.1", - "proc-log": "^4.2.0", + "make-fetch-happen": "^14.0.1", + "proc-log": "^5.0.0", "promise-retry": "^2.0.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@sigstore/tuf": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", - "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-3.0.0.tgz", + "integrity": "sha512-9Xxy/8U5OFJu7s+OsHzI96IX/OzjF/zj0BSSaWhgJgTqtlBhQIV2xdrQI5qxLD7+CWWDepadnXAxzaZ3u9cvRw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.3.2", - "tuf-js": "^2.2.1" + "tuf-js": "^3.0.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@sigstore/verify": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", - "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-2.0.0.tgz", + "integrity": "sha512-Ggtq2GsJuxFNUvQzLoXqRwS4ceRfLAJnrIHUDrzAD0GgnOhwujJkKkxM/s5Bako07c3WtAs/sZo5PJq7VHjeDg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.1.0", + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", "@sigstore/protobuf-specs": "^0.3.2" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@sinclair/typebox": { @@ -6660,6 +7155,7 @@ "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -6864,17 +7360,17 @@ } }, "node_modules/@tufjs/models": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", - "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-3.0.1.tgz", + "integrity": "sha512-UUYHISyhCU3ZgN8yaear3cGATHb3SMuKHsQ/nVbHXcmnBf+LzQ/cQfhNG+rfaSHgqGKNEm2cOCLVLELStUQ1JA==", "dev": true, "license": "MIT", "dependencies": { "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.4" + "minimatch": "^9.0.5" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/@tybys/wasm-util": { @@ -6942,6 +7438,7 @@ "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -6953,6 +7450,7 @@ "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } @@ -6963,6 +7461,7 @@ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } @@ -6973,6 +7472,7 @@ "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/express-serve-static-core": "*", "@types/node": "*" @@ -6993,9 +7493,9 @@ "license": "MIT" }, "node_modules/@types/d3-shape": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", - "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", "dev": true, "license": "MIT", "dependencies": { @@ -7019,6 +7519,30 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -7032,6 +7556,7 @@ "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -7040,11 +7565,12 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.3.tgz", - "integrity": "sha512-JEhMNwUJt7bw728CydvYzntD0XJeTmDnvwLlbfbAhE7Tbslm/ax6bdIiUwTgeVlZTsJQPwZwKpAkyDtIjsvx3g==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.4.tgz", + "integrity": "sha512-5kz9ScmzBdzTgB/3susoCgfqNDzBjvLL4taparufgSvlwjdLy6UyUy9T/tCpYd2GIdIilCatC4iSQS0QSYHt0w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -7058,6 +7584,7 @@ "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -7090,7 +7617,8 @@ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/http-proxy": { "version": "1.17.15", @@ -7098,6 +7626,7 @@ "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } @@ -7241,17 +7770,8 @@ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true, - "license": "MIT" - }, - "node_modules/@types/mute-stream": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", - "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", - "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*" - } + "peer": true }, "node_modules/@types/node": { "version": "22.10.5", @@ -7269,6 +7789,7 @@ "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } @@ -7286,28 +7807,28 @@ "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "license": "MIT" + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" }, "node_modules/@types/qs": { "version": "6.9.17", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/react": { "version": "18.3.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", - "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -7318,7 +7839,8 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/semver": { "version": "7.5.8", @@ -7333,6 +7855,7 @@ "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -7344,6 +7867,7 @@ "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/express": "*" } @@ -7354,6 +7878,7 @@ "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/http-errors": "*", "@types/node": "*", @@ -7373,6 +7898,7 @@ "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } @@ -7438,19 +7964,13 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/wrap-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", - "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/ws": { "version": "8.5.13", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*" } @@ -7473,21 +7993,21 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.0.tgz", - "integrity": "sha512-NggSaEZCdSrFddbctrVjkVZvFC6KGfKfNK0CU7mNK/iKHGKbzT4Wmgm08dKpcZECBu9f5FypndoMyRHkdqfT1Q==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.1.tgz", + "integrity": "sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/type-utils": "8.19.0", - "@typescript-eslint/utils": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.19.1", + "@typescript-eslint/type-utils": "8.19.1", + "@typescript-eslint/utils": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7513,16 +8033,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.0.tgz", - "integrity": "sha512-6M8taKyOETY1TKHp0x8ndycipTVgmp4xtg5QpEZzXxDhNvvHOJi5rLRkLr8SK3jTgD5l4fTlvBiRdfsuWydxBw==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.19.1.tgz", + "integrity": "sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/scope-manager": "8.19.1", + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/typescript-estree": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1", "debug": "^4.3.4" }, "engines": { @@ -7538,14 +8058,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.0.tgz", - "integrity": "sha512-hkoJiKQS3GQ13TSMEiuNmSCvhz7ujyqD1x3ShbaETATHrck+9RaDdUbt+osXaUuns9OFwrDTTrjtwsU8gJyyRA==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.19.1.tgz", + "integrity": "sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0" + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7556,16 +8076,16 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.0.tgz", - "integrity": "sha512-TZs0I0OSbd5Aza4qAMpp1cdCYVnER94IziudE3JU328YUHgWu9gwiwhag+fuLeJ2LkWLXI+F/182TbG+JaBdTg==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.19.1.tgz", + "integrity": "sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.19.0", - "@typescript-eslint/utils": "8.19.0", + "@typescript-eslint/typescript-estree": "8.19.1", + "@typescript-eslint/utils": "8.19.1", "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7580,9 +8100,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.0.tgz", - "integrity": "sha512-8XQ4Ss7G9WX8oaYvD4OOLCjIQYgRQxO+qCiR2V2s2GxI9AUpo7riNwo6jDhKtTcaJjT8PY54j2Yb33kWtSJsmA==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.19.1.tgz", + "integrity": "sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==", "dev": true, "license": "MIT", "engines": { @@ -7594,20 +8114,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.0.tgz", - "integrity": "sha512-WW9PpDaLIFW9LCbucMSdYUuGeFUz1OkWYS/5fwZwTA+l2RwlWFdJvReQqMUMBw4yJWJOfqd7An9uwut2Oj8sLw==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.19.1.tgz", + "integrity": "sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/visitor-keys": "8.19.0", + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/visitor-keys": "8.19.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7621,16 +8141,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.0.tgz", - "integrity": "sha512-PTBG+0oEMPH9jCZlfg07LCB2nYI0I317yyvXGfxnvGvw4SHIOuRnQ3kadyyXY6tGdChusIHIbM5zfIbp4M6tCg==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.19.1.tgz", + "integrity": "sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.19.0", - "@typescript-eslint/types": "8.19.0", - "@typescript-eslint/typescript-estree": "8.19.0" + "@typescript-eslint/scope-manager": "8.19.1", + "@typescript-eslint/types": "8.19.1", + "@typescript-eslint/typescript-estree": "8.19.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7645,13 +8165,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.0.tgz", - "integrity": "sha512-mCFtBbFBJDCNCWUl5y6sZSCHXw1DEFEk3c/M3nRK2a4XUB8StGFtmcEMizdjKuBzB6e/smJAAWYug3VrdLMr1w==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.19.1.tgz", + "integrity": "sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.19.0", + "@typescript-eslint/types": "8.19.1", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -7731,16 +8251,6 @@ } } }, - "node_modules/@vitest/mocker/node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, "node_modules/@vitest/pretty-format": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", @@ -7783,16 +8293,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/snapshot/node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, "node_modules/@vitest/spy": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", @@ -7842,6 +8342,7 @@ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -7852,21 +8353,24 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", @@ -7874,6 +8378,7 @@ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -7885,7 +8390,8 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", @@ -7893,6 +8399,7 @@ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -7906,6 +8413,7 @@ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -7916,6 +8424,7 @@ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -7925,7 +8434,8 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", @@ -7933,6 +8443,7 @@ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -7950,6 +8461,7 @@ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -7964,6 +8476,7 @@ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -7977,6 +8490,7 @@ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -7992,6 +8506,7 @@ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -8002,14 +8517,16 @@ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true, - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", @@ -8092,6 +8609,7 @@ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -8106,6 +8624,7 @@ "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -8116,6 +8635,7 @@ "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mime-db": "^1.53.0" }, @@ -8123,16 +8643,6 @@ "node": ">= 0.6" } }, - "node_modules/accepts/node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -8146,16 +8656,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -8185,6 +8685,7 @@ "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "loader-utils": "^2.0.0", "regex-parser": "^2.2.11" @@ -8199,6 +8700,7 @@ "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -8218,20 +8720,6 @@ "node": ">= 14" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -8273,6 +8761,7 @@ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -8315,6 +8804,7 @@ "node >= 0.8.0" ], "license": "Apache-2.0", + "peer": true, "bin": { "ansi-html": "bin/ansi-html" } @@ -8398,7 +8888,8 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz", "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/array-union": { "version": "2.1.0", @@ -8464,6 +8955,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "browserslist": "^4.23.3", "caniuse-lite": "^1.0.30001646", @@ -8537,11 +9029,12 @@ } }, "node_modules/babel-loader": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", - "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", + "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "find-cache-dir": "^4.0.0", "schema-utils": "^4.0.0" @@ -8610,6 +9103,7 @@ "integrity": "sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.6.3", @@ -8625,6 +9119,7 @@ "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.2", "core-js-compat": "^3.38.0" @@ -8639,6 +9134,7 @@ "integrity": "sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.3" }, @@ -8751,7 +9247,25 @@ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true + }, + "node_modules/beasties": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.1.0.tgz", + "integrity": "sha512-+Ssscd2gVG24qRNC+E2g88D+xsQW4xwakWtKAiGEQ3Pw54/FGdyo9RrfxhGhEv6ilFVbB7r3Lgx+QnAxnSpECw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "htmlparser2": "^9.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-media-query-parser": "^0.2.3" + } }, "node_modules/big.js": { "version": "5.2.2", @@ -8759,6 +9273,7 @@ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "*" } @@ -8769,6 +9284,7 @@ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=8" }, @@ -8809,6 +9325,7 @@ "integrity": "sha512-SNMk0OONlQ01uk8EPeiBvTW7W4ovpL5b1O3t1sjpPgfxOQ6BqQJ6XjxinDPR79Z6HdcD5zBBwr5ssiTlgdNztQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -8831,6 +9348,7 @@ "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -8844,6 +9362,7 @@ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -8854,6 +9373,7 @@ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -8868,6 +9388,7 @@ "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" @@ -8922,9 +9443,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", - "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "funding": [ { "type": "opencollective", @@ -9014,6 +9535,7 @@ "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "run-applescript": "^7.0.0" }, @@ -9030,6 +9552,7 @@ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -9045,13 +9568,13 @@ } }, "node_modules/cacache": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", - "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz", + "integrity": "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/fs": "^3.1.0", + "@npmcli/fs": "^4.0.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", "lru-cache": "^10.0.1", @@ -9059,13 +9582,23 @@ "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" } }, "node_modules/cacache/node_modules/glob": { @@ -9096,6 +9629,50 @@ "dev": true, "license": "ISC" }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -9176,9 +9753,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001690", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz", - "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==", + "version": "1.0.30001692", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", + "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", "funding": [ { "type": "opencollective", @@ -9257,41 +9834,18 @@ } }, "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" } }, "node_modules/chownr": { @@ -9310,6 +9864,7 @@ "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.0" } @@ -9337,16 +9892,6 @@ "dev": true, "license": "MIT" }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -9479,6 +10024,7 @@ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -9494,6 +10040,7 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "isobject": "^3.0.1" }, @@ -9588,7 +10135,8 @@ "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/compare-versions": { "version": "6.1.1", @@ -9602,6 +10150,7 @@ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mime-db": ">= 1.43.0 < 2" }, @@ -9615,6 +10164,7 @@ "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", @@ -9628,6 +10178,17 @@ "node": ">= 0.8.0" } }, + "node_modules/compression/node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/compression/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9647,7 +10208,8 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/concat-map": { "version": "0.0.1", @@ -9662,6 +10224,7 @@ "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.8" } @@ -9672,6 +10235,7 @@ "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "safe-buffer": "5.2.1" }, @@ -9698,7 +10262,8 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/content-type": { "version": "1.0.5", @@ -9706,6 +10271,7 @@ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -9722,6 +10288,7 @@ "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -9732,6 +10299,7 @@ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.6.0" } @@ -9742,6 +10310,7 @@ "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "is-what": "^3.14.1" }, @@ -9755,6 +10324,7 @@ "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-glob": "^3.3.2", "glob-parent": "^6.0.1", @@ -9775,9 +10345,9 @@ } }, "node_modules/core-js": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", - "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", + "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -9786,13 +10356,14 @@ } }, "node_modules/core-js-compat": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", - "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", + "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "browserslist": "^4.24.2" + "browserslist": "^4.24.3" }, "funding": { "type": "opencollective", @@ -9811,6 +10382,7 @@ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -9861,23 +10433,6 @@ "dev": true, "license": "MIT" }, - "node_modules/critters": { - "version": "0.0.25", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.25.tgz", - "integrity": "sha512-ROF/tjJyyRdM8/6W0VqoN5Ql05xAGnkf5b7f3sTEl1bI5jTQQf8O918RD/V9tEb9pRY/TKcvJekDbJtniHyPtQ==", - "deprecated": "Ownership of Critters has moved to the Nuxt team, who will be maintaining the project going forward. If you'd like to keep using Critters, please switch to the actively-maintained fork at https://github.com/danielroe/beasties", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "chalk": "^4.1.0", - "css-select": "^5.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.2", - "htmlparser2": "^8.0.2", - "postcss": "^8.4.23", - "postcss-media-query-parser": "^0.2.3" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -9914,6 +10469,7 @@ "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", @@ -9991,6 +10547,7 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "cssesc": "bin/cssesc" }, @@ -10006,18 +10563,26 @@ "license": "MIT" }, "node_modules/cssstyle": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", - "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.2.1.tgz", + "integrity": "sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==", "dev": true, "license": "MIT", "dependencies": { - "rrweb-cssom": "^0.7.1" + "@asamuzakjp/css-color": "^2.8.2", + "rrweb-cssom": "^0.8.0" }, "engines": { "node": ">=18" } }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -10385,6 +10950,7 @@ "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" @@ -10402,6 +10968,7 @@ "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -10446,6 +11013,7 @@ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -10469,6 +11037,7 @@ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -10490,6 +11059,7 @@ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -10501,6 +11071,7 @@ "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "dev": true, "license": "Apache-2.0", + "optional": true, "engines": { "node": ">=8" } @@ -10520,7 +11091,8 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/diff": { "version": "4.0.2", @@ -10577,6 +11149,7 @@ "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" }, @@ -10646,9 +11219,9 @@ } }, "node_modules/domutils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.1.tgz", - "integrity": "sha512-xWXmuRnN9OMP6ptPd2+H0cCbcYBULa5YDTbMm/2lvkWvNA3O4wcW+GvzooqBuNM8yy6pl3VIAeJTUUWUbfI5Fw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -10716,7 +11289,8 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/ejs": { "version": "3.1.10", @@ -10735,9 +11309,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.76", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.76.tgz", - "integrity": "sha512-CjVQyG7n7Sr+eBXE86HIulnL5N8xZY1sgmOPGuq/F0Rr0FJq63lg0kEtOIDfZBk44FnDLf6FUJ+dsJcuiUDdDQ==", + "version": "1.5.80", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz", + "integrity": "sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==", "license": "ISC" }, "node_modules/emittery": { @@ -10781,6 +11355,7 @@ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 4" } @@ -10791,6 +11366,7 @@ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -10836,6 +11412,7 @@ "integrity": "sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -10906,6 +11483,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "prr": "~1.0.1" }, @@ -10964,9 +11542,9 @@ } }, "node_modules/esbuild": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.0.tgz", + "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -10977,36 +11555,36 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.0", - "@esbuild/android-arm": "0.23.0", - "@esbuild/android-arm64": "0.23.0", - "@esbuild/android-x64": "0.23.0", - "@esbuild/darwin-arm64": "0.23.0", - "@esbuild/darwin-x64": "0.23.0", - "@esbuild/freebsd-arm64": "0.23.0", - "@esbuild/freebsd-x64": "0.23.0", - "@esbuild/linux-arm": "0.23.0", - "@esbuild/linux-arm64": "0.23.0", - "@esbuild/linux-ia32": "0.23.0", - "@esbuild/linux-loong64": "0.23.0", - "@esbuild/linux-mips64el": "0.23.0", - "@esbuild/linux-ppc64": "0.23.0", - "@esbuild/linux-riscv64": "0.23.0", - "@esbuild/linux-s390x": "0.23.0", - "@esbuild/linux-x64": "0.23.0", - "@esbuild/netbsd-x64": "0.23.0", - "@esbuild/openbsd-arm64": "0.23.0", - "@esbuild/openbsd-x64": "0.23.0", - "@esbuild/sunos-x64": "0.23.0", - "@esbuild/win32-arm64": "0.23.0", - "@esbuild/win32-ia32": "0.23.0", - "@esbuild/win32-x64": "0.23.0" + "@esbuild/aix-ppc64": "0.24.0", + "@esbuild/android-arm": "0.24.0", + "@esbuild/android-arm64": "0.24.0", + "@esbuild/android-x64": "0.24.0", + "@esbuild/darwin-arm64": "0.24.0", + "@esbuild/darwin-x64": "0.24.0", + "@esbuild/freebsd-arm64": "0.24.0", + "@esbuild/freebsd-x64": "0.24.0", + "@esbuild/linux-arm": "0.24.0", + "@esbuild/linux-arm64": "0.24.0", + "@esbuild/linux-ia32": "0.24.0", + "@esbuild/linux-loong64": "0.24.0", + "@esbuild/linux-mips64el": "0.24.0", + "@esbuild/linux-ppc64": "0.24.0", + "@esbuild/linux-riscv64": "0.24.0", + "@esbuild/linux-s390x": "0.24.0", + "@esbuild/linux-x64": "0.24.0", + "@esbuild/netbsd-x64": "0.24.0", + "@esbuild/openbsd-arm64": "0.24.0", + "@esbuild/openbsd-x64": "0.24.0", + "@esbuild/sunos-x64": "0.24.0", + "@esbuild/win32-arm64": "0.24.0", + "@esbuild/win32-ia32": "0.24.0", + "@esbuild/win32-x64": "0.24.0" } }, "node_modules/esbuild-wasm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.23.0.tgz", - "integrity": "sha512-6jP8UmWy6R6TUUV8bMuC3ZyZ6lZKI56x0tkxyCIqWwRRJ/DgeQKneh/Oid5EoGoPFLrGNkz47ZEtWAYuiY/u9g==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.24.0.tgz", + "integrity": "sha512-xhNn5tL1AhkPg4ft59yXT6FkwKXiPSYyz1IeinJHUJpjvOHOIPvdmFQc0pGdjxlKSbzZc2mNmtVOWAR1EF/JAg==", "dev": true, "license": "MIT", "bin": { @@ -11030,7 +11608,8 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -11046,19 +11625,19 @@ } }, "node_modules/eslint": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", - "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", + "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", + "@eslint/core": "^0.10.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.17.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/js": "9.18.0", + "@eslint/plugin-kit": "^0.2.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.1", @@ -11277,6 +11856,19 @@ "node": ">=8" } }, + "node_modules/eslint-plugin-deprecation/node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/eslint-plugin-jest": { "version": "28.10.0", "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.10.0.tgz", @@ -11728,6 +12320,7 @@ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -11737,7 +12330,8 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/events": { "version": "3.3.0", @@ -11745,6 +12339,7 @@ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.8.x" } @@ -11847,6 +12442,7 @@ "integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.0.1", @@ -11891,6 +12487,7 @@ "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -11901,6 +12498,7 @@ "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mime-db": "^1.53.0" }, @@ -11927,7 +12525,8 @@ "url": "https://feross.org/support" } ], - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/external-editor": { "version": "3.1.0", @@ -11962,6 +12561,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -11978,6 +12578,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -12007,9 +12608,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.4.tgz", - "integrity": "sha512-G3iTQw1DizJQ5eEqj1CbFCWhq+pzum7qepkxU7rS1FGZDqjYKcrguo9XDRbV7EgPnn8CgaPigTq+NEjyioeYZQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.5.tgz", + "integrity": "sha512-5JnBCWpFlMo0a3ciDy/JckMzzv1U9coZrIhedq+HXxxUfDTAiS0LA8OKVao4G9BxmCVck/jtA5r3KAtRWEyD8Q==", "dev": true, "funding": [ { @@ -12134,6 +12735,7 @@ "integrity": "sha512-MX6Zo2adDViYh+GcxxB1dpO43eypOGUOL12rLCOTMQv/DfIbpSJUy4oQIIZhVZkH9e+bZWKMon0XHFEju16tkQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -12153,6 +12755,7 @@ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -12163,6 +12766,7 @@ "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "common-path-prefix": "^3.0.0", "pkg-dir": "^7.0.0" @@ -12233,14 +12837,14 @@ "license": "ISC" }, "node_modules/folder-hash": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/folder-hash/-/folder-hash-4.1.0.tgz", - "integrity": "sha512-45DBqW/wE1FdEynQ922zoKsICdAkZEkuyWijChitENaoI+AF/FsTlKOB+EwaEqhNfBZ5dEgLVv694wbpNMIC5g==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/folder-hash/-/folder-hash-4.1.1.tgz", + "integrity": "sha512-1ZSlKJSbET3XpglnEXC9g+QF4QRZhqHIjpFfa4pAMfO4tu/XYPafpeHEX6zOFS2EolOIXr0lPh1eSjmdWItX2w==", "dev": true, "license": "MIT", "dependencies": { "debug": "4.4.0", - "minimatch": "10.0.1" + "minimatch": "7.4.6" }, "bin": { "folder-hash": "bin/folder-hash" @@ -12250,16 +12854,16 @@ } }, "node_modules/folder-hash/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": "20 || >=22" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -12324,6 +12928,7 @@ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -12334,6 +12939,7 @@ "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "*" }, @@ -12361,6 +12967,7 @@ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -12665,6 +13272,7 @@ "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@sindresorhus/merge-streams": "^2.1.0", "fast-glob": "^3.3.2", @@ -12686,6 +13294,7 @@ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 4" } @@ -12747,7 +13356,8 @@ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/has-flag": { "version": "4.0.0", @@ -12823,16 +13433,16 @@ "license": "MIT" }, "node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.0.2.tgz", + "integrity": "sha512-sYKnA7eGln5ov8T8gnYlkSOxFJvywzEx9BueN6xo/GKO8PGiI6uK6xx+DIGe45T3bdVjLAQDQW1aicT8z8JwQg==", "dev": true, "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/hosted-git-info/node_modules/lru-cache": { @@ -12848,6 +13458,7 @@ "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", @@ -12882,9 +13493,9 @@ "license": "MIT" }, "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", @@ -12897,8 +13508,8 @@ "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "domutils": "^3.1.0", + "entities": "^4.5.0" } }, "node_modules/http-cache-semantics": { @@ -12913,7 +13524,8 @@ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/http-errors": { "version": "2.0.0", @@ -12921,6 +13533,7 @@ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -12933,9 +13546,9 @@ } }, "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz", + "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==", "license": "MIT" }, "node_modules/http-proxy": { @@ -12944,6 +13557,7 @@ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -12973,6 +13587,7 @@ "integrity": "sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/http-proxy": "^1.17.15", "debug": "^4.3.6", @@ -13031,6 +13646,7 @@ "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10.18" } @@ -13054,6 +13670,7 @@ "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": "^10 || ^12 || >= 14" }, @@ -13093,16 +13710,16 @@ } }, "node_modules/ignore-walk": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", - "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-7.0.0.tgz", + "integrity": "sha512-T4gbf83A4NH95zvhVYZc+qWocBBGlpzUXLPGurJggw/WIOwicfXJChLDP/iBZnN5WqROSu5Bm3hhle4z8a8YGQ==", "dev": true, "license": "ISC", "dependencies": { "minimatch": "^9.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/image-size": { @@ -13112,6 +13729,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "bin": { "image-size": "bin/image-size.js" }, @@ -13248,16 +13866,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -13277,13 +13885,13 @@ "license": "ISC" }, "node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/interactjs": { @@ -13324,6 +13932,7 @@ "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 10" } @@ -13341,6 +13950,7 @@ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -13370,6 +13980,7 @@ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "is-docker": "cli.js" }, @@ -13430,6 +14041,7 @@ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "is-docker": "^3.0.0" }, @@ -13453,13 +14065,6 @@ "node": ">=8" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/is-mobile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-4.0.0.tgz", @@ -13472,6 +14077,7 @@ "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=16" }, @@ -13494,6 +14100,7 @@ "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -13507,6 +14114,7 @@ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -13523,7 +14131,8 @@ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/is-stream": { "version": "2.0.1", @@ -13556,7 +14165,8 @@ "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/is-wsl": { "version": "3.1.0", @@ -13564,6 +14174,7 @@ "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "is-inside-container": "^1.0.0" }, @@ -13600,6 +14211,7 @@ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -14459,17 +15071,17 @@ } }, "node_modules/jest-preset-angular": { - "version": "14.4.2", - "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.4.2.tgz", - "integrity": "sha512-BYYv0FaTDfBNh8WyA9mpOV3krfw20kurBGK8INZUnv7KZDAWZuQtCET4TwTWxSNQ9jS1OX1+a5weCm/bTDDM1A==", + "version": "14.5.0", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.5.0.tgz", + "integrity": "sha512-L7eScW3IX5pZi2GRi0/q6Eo58qf76bdK93ozZxOI2gSK2fE+nnSIirkl2zvZ21AiJM5f4IawwBcqBhAPDvOs+Q==", "dev": true, "license": "MIT", "dependencies": { "bs-logger": "^0.2.6", "esbuild-wasm": ">=0.15.13", - "jest-environment-jsdom": "^29.0.0", - "jest-util": "^29.0.0", - "pretty-format": "^29.0.0", + "jest-environment-jsdom": "^29.7.0", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0", "ts-jest": "^29.0.0" }, "engines": { @@ -14483,7 +15095,13 @@ "@angular/core": ">=15.0.0 <20.0.0", "@angular/platform-browser-dynamic": ">=15.0.0 <20.0.0", "jest": "^29.0.0", + "jsdom": ">=20.0.0", "typescript": ">=4.8" + }, + "peerDependenciesMeta": { + "jsdom": { + "optional": true + } } }, "node_modules/jest-preset-angular/node_modules/ansi-styles": { @@ -14896,6 +15514,7 @@ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -14974,15 +15593,15 @@ } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { @@ -14993,13 +15612,13 @@ "license": "MIT" }, "node_modules/json-parse-even-better-errors": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", - "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", "dev": true, "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/json-schema-traverse": { @@ -15106,6 +15725,7 @@ "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "source-map-support": "^0.5.5" } @@ -15142,6 +15762,7 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -15172,6 +15793,7 @@ "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "picocolors": "^1.0.0", "shell-quote": "^1.8.1" @@ -15183,6 +15805,7 @@ "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -15210,6 +15833,7 @@ "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 18.12.0" }, @@ -15238,6 +15862,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" @@ -15253,6 +15878,7 @@ "dev": true, "license": "BSD-3-Clause", "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -15287,6 +15913,7 @@ "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "webpack-sources": "^3.0.0" }, @@ -15368,32 +15995,6 @@ "url": "https://opencollective.com/lint-staged" } }, - "node_modules/lint-staged/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/lint-staged/node_modules/chalk": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", @@ -15417,13 +16018,6 @@ "node": ">=18" } }, - "node_modules/lint-staged/node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true, - "license": "MIT" - }, "node_modules/lint-staged/node_modules/execa": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", @@ -15484,24 +16078,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/listr2": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", - "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^4.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/lint-staged/node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -15560,22 +16136,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/lint-staged/node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", @@ -15589,28 +16149,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/listr2": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", - "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", + "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -15693,29 +16235,30 @@ } }, "node_modules/lmdb": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.13.tgz", - "integrity": "sha512-UGe+BbaSUQtAMZobTb4nHvFMrmvuAQKSeaqAX2meTEQjfsbpl5sxdHD8T72OnwD4GU9uwNhYXIVe4QGs8N9Zyw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.1.5.tgz", + "integrity": "sha512-46Mch5Drq+A93Ss3gtbg+Xuvf5BOgIuvhKDWoGa3HcPHI6BL2NCOkRdSx1D4VfzwrxhnsjbyIVsLRlQHu6URvw==", "dev": true, "hasInstallScript": true, "license": "MIT", + "optional": true, "dependencies": { - "msgpackr": "^1.10.2", + "msgpackr": "^1.11.2", "node-addon-api": "^6.1.0", "node-gyp-build-optional-packages": "5.2.2", - "ordered-binary": "^1.4.1", + "ordered-binary": "^1.5.3", "weak-lru-cache": "^1.2.2" }, "bin": { "download-lmdb-prebuilds": "bin/download-prebuilds.js" }, "optionalDependencies": { - "@lmdb/lmdb-darwin-arm64": "3.0.13", - "@lmdb/lmdb-darwin-x64": "3.0.13", - "@lmdb/lmdb-linux-arm": "3.0.13", - "@lmdb/lmdb-linux-arm64": "3.0.13", - "@lmdb/lmdb-linux-x64": "3.0.13", - "@lmdb/lmdb-win32-x64": "3.0.13" + "@lmdb/lmdb-darwin-arm64": "3.1.5", + "@lmdb/lmdb-darwin-x64": "3.1.5", + "@lmdb/lmdb-linux-arm": "3.1.5", + "@lmdb/lmdb-linux-arm64": "3.1.5", + "@lmdb/lmdb-linux-x64": "3.1.5", + "@lmdb/lmdb-win32-x64": "3.1.5" } }, "node_modules/loader-runner": { @@ -15724,6 +16267,7 @@ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6.11.5" } @@ -15734,6 +16278,7 @@ "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 12.13.0" } @@ -15771,7 +16316,8 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/lodash.memoize": { "version": "4.1.2", @@ -15973,9 +16519,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, "license": "MIT", "dependencies": { @@ -16006,27 +16552,26 @@ "license": "ISC" }, "node_modules/make-fetch-happen": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", - "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz", + "integrity": "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", + "minipass-fetch": "^4.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", "promise-retry": "^2.0.1", - "ssri": "^10.0.0" + "ssri": "^12.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/makeerror": { @@ -16099,16 +16644,18 @@ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/memfs": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.15.3.tgz", - "integrity": "sha512-vR/g1SgqvKJgAyYla+06G4p/EOcEmwhYuVb1yc1ixcKf8o/sh7Zngv63957ZSNd1xrZJoinmNyDf2LzuP8WJXw==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.0.tgz", + "integrity": "sha512-4eirfZ7thblFmqFjywlTmuWVSvccHAJbn1r8qQLzmTO11qcqpohOjmY2mFce6x7x7WtskzRqApPD0hv+Oa74jg==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@jsonjoy.com/json-pack": "^1.0.3", "@jsonjoy.com/util": "^1.3.0", @@ -16129,6 +16676,7 @@ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -16158,6 +16706,7 @@ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -16194,6 +16743,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "bin": { "mime": "cli.js" }, @@ -16248,11 +16798,12 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", - "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", + "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -16273,7 +16824,8 @@ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/minimatch": { "version": "9.0.5", @@ -16325,18 +16877,18 @@ } }, "node_modules/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.0.tgz", + "integrity": "sha512-2v6aXUXwLP1Epd/gc32HAMIWoczx+fZwEPRHm/VwtrJzRGwR1qGZXEYV3Zp8ZjjbwaZhMrM6uHV4KVkk+XCc2w==", "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "minizlib": "^3.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" }, "optionalDependencies": { "encoding": "^0.1.13" @@ -16442,39 +16994,19 @@ "license": "ISC" }, "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz", + "integrity": "sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==", "dev": true, "license": "MIT", "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" + "minipass": "^7.0.4", + "rimraf": "^5.0.5" }, "engines": { - "node": ">=8" + "node": ">= 18" } }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -16532,6 +17064,7 @@ "integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==", "dev": true, "license": "MIT", + "optional": true, "optionalDependencies": { "msgpackr-extract": "^3.0.2" } @@ -16565,6 +17098,7 @@ "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "dns-packet": "^5.2.2", "thunky": "^1.0.2" @@ -16574,13 +17108,13 @@ } }, "node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/n-gram": { @@ -16626,6 +17160,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" @@ -16644,6 +17179,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -16652,9 +17188,9 @@ } }, "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "dev": true, "license": "MIT", "engines": { @@ -16666,7 +17202,8 @@ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/ng-mocks": { "version": "14.13.1", @@ -16685,29 +17222,29 @@ } }, "node_modules/ngx-infinite-scroll": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-18.0.0.tgz", - "integrity": "sha512-D183TDwpsd9Zl56UmItsl3RzHdN25srAISfg6lc3A8mEKkEgOq0s7ZzRAYcx8DHsAkMgtZqjIPEvMifD3DOB/g==", + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-19.0.0.tgz", + "integrity": "sha512-Ft4xNNDLXoDGi2hF6ylehjxbG8JIgfoL6qDWWcebGMcbh1CEfEsh0HGkDuFlX/cBBMenRh2HFbXlYq8BAtbvLw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": ">=18.0.0 <19.0.0", - "@angular/core": ">=18.0.0 <19.0.0" + "@angular/common": ">=19.0.0 <20.0.0", + "@angular/core": ">=19.0.0 <20.0.0" } }, "node_modules/ngx-webstorage": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/ngx-webstorage/-/ngx-webstorage-18.0.0.tgz", - "integrity": "sha512-Fg9y0Vb89CBSjSmJPjvotZljyqSNo97TlWpR6OIsHzuMwrj7u9OgEBmqOgiRK2GGsUdVMy8SF3s3Ymier6xuew==", + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/ngx-webstorage/-/ngx-webstorage-19.0.1.tgz", + "integrity": "sha512-5Z7YowQKvmABb2d80AavOo+DSRu+bduxDpDTc7go7clJyMtIwidINa3UC3m13LgkBc8QJj9K8LkY0ugwTmYDdA==", "license": "MIT", "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { - "@angular/common": "^18.0.0", - "@angular/core": "^18.0.0" + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0" } }, "node_modules/ngxtension": { @@ -16738,6 +17275,13 @@ } } }, + "node_modules/ngxtension/node_modules/@angular-eslint/bundled-angular-compiler": { + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-18.4.3.tgz", + "integrity": "sha512-zdrA8mR98X+U4YgHzUKmivRU+PxzwOL/j8G7eTOvBuq8GPzsP+hvak+tyxlgeGm9HsvpFj9ERHLtJ0xDUPs8fg==", + "dev": true, + "license": "MIT" + }, "node_modules/ngxtension/node_modules/@ts-morph/common": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.23.0.tgz", @@ -16785,36 +17329,13 @@ "code-block-writer": "^13.0.1" } }, - "node_modules/nice-napi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", - "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "!win32" - ], - "dependencies": { - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.2" - } - }, - "node_modules/nice-napi/node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/node-addon-api": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/node-forge": { "version": "1.3.1", @@ -16822,14 +17343,15 @@ "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true, "license": "(BSD-3-Clause OR GPL-2.0)", + "peer": true, "engines": { "node": ">= 6.13.0" } }, "node_modules/node-gyp": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.3.1.tgz", - "integrity": "sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.0.0.tgz", + "integrity": "sha512-zQS+9MTTeCMgY0F3cWPyJyRFAkVltQ1uXm+xXu/ES6KFgC6Czo1Seb9vQW2wNxSX2OrDTiqL0ojtkFxBQ0ypIw==", "dev": true, "license": "MIT", "dependencies": { @@ -16837,18 +17359,18 @@ "exponential-backoff": "^3.1.1", "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^13.0.0", - "nopt": "^7.0.0", - "proc-log": "^4.1.0", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", "semver": "^7.3.5", - "tar": "^6.2.1", - "which": "^4.0.0" + "tar": "^7.4.3", + "which": "^5.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/node-gyp-build": { @@ -16869,6 +17391,7 @@ "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { "detect-libc": "^2.0.1" }, @@ -16878,6 +17401,16 @@ "node-gyp-build-optional-packages-test": "build-test.js" } }, + "node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, "node_modules/node-gyp/node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -16909,10 +17442,44 @@ "node": ">=16" } }, + "node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/node-gyp/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", "dev": true, "license": "ISC", "dependencies": { @@ -16922,7 +17489,17 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" } }, "node_modules/node-int64": { @@ -16946,9 +17523,9 @@ "license": "MIT" }, "node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.0.0.tgz", + "integrity": "sha512-1L/fTJ4UmV/lUxT2Uf006pfZKTvAgCF+chz+0OgBHO8u2Z67pE7AaAUUj7CJy0lXqHmymUvGFt6NE9R3HER0yw==", "dev": true, "license": "ISC", "dependencies": { @@ -16958,22 +17535,22 @@ "nopt": "bin/nopt.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/normalize-package-data": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", - "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-7.0.0.tgz", + "integrity": "sha512-k6U0gKRIuNCTkwHGZqblCfLfBRh+w1vI6tBo+IeJwq2M8FUiOqhX7GH+GArQGScA7azd1WfyRCvxoXDO3hQDIA==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "hosted-git-info": "^7.0.0", + "hosted-git-info": "^8.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/normalize-path": { @@ -16992,109 +17569,110 @@ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/npm-bundled": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", - "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz", + "integrity": "sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==", "dev": true, "license": "ISC", "dependencies": { - "npm-normalize-package-bin": "^3.0.0" + "npm-normalize-package-bin": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-install-checks": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", - "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.1.tgz", + "integrity": "sha512-u6DCwbow5ynAX5BdiHQ9qvexme4U3qHW3MWe5NqH+NeBm0LbiH6zvGjNNew1fY+AZZUtVHbOPF3j7mJxbUzpXg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "semver": "^7.1.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", - "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-package-arg": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", - "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.0.tgz", + "integrity": "sha512-ZTE0hbwSdTNL+Stx2zxSqdu2KZfNDcrtrLdIk7XGnQFYBWYDho/ORvXtn5XEePcL3tFpGjHCV3X3xrtDh7eZ+A==", "dev": true, "license": "ISC", "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" + "validate-npm-package-name": "^6.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-packlist": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", - "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-9.0.0.tgz", + "integrity": "sha512-8qSayfmHJQTx3nJWYbbUmflpyarbLMBc6LCAjYsiGtXxDB68HaZpb8re6zeaLGxZzDuMdhsg70jryJe+RrItVQ==", "dev": true, "license": "ISC", "dependencies": { - "ignore-walk": "^6.0.4" + "ignore-walk": "^7.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-pick-manifest": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", - "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", + "integrity": "sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==", "dev": true, "license": "ISC", "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^11.0.0", + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-registry-fetch": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", - "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-18.0.2.tgz", + "integrity": "sha512-LeVMZBBVy+oQb5R6FDV9OlJCcWDU+al10oKpe+nsvcHnG24Z3uM3SvJYKfGJlfGjVU8v9liejCrUR/M5HO5NEQ==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/redact": "^2.0.0", + "@npmcli/redact": "^3.0.0", "jsonparse": "^1.3.1", - "make-fetch-happen": "^13.0.0", + "make-fetch-happen": "^14.0.0", "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minizlib": "^2.1.2", - "npm-package-arg": "^11.0.0", - "proc-log": "^4.0.0" + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-run-path": { @@ -17131,9 +17709,9 @@ "license": "MIT" }, "node_modules/nx": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/nx/-/nx-20.3.0.tgz", - "integrity": "sha512-Nzi4k7tV22zwO2iBLk+pHxorLEWPJpPrVCACtz0SQ63j/LiAgfhoqruJO+VU+V+E9qdyPsvmqIL/Iaf/GRQlqA==", + "version": "20.3.1", + "resolved": "https://registry.npmjs.org/nx/-/nx-20.3.1.tgz", + "integrity": "sha512-pO48DoQAwVKBEF7/od3bc1tHBYfafgiuS/hHX3yGmhpWW58baIlxMWFp6QY9+A9Q0R+26pd6AEGnE7d1f7+i/g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -17178,16 +17756,16 @@ "nx-cloud": "bin/nx-cloud.js" }, "optionalDependencies": { - "@nx/nx-darwin-arm64": "20.3.0", - "@nx/nx-darwin-x64": "20.3.0", - "@nx/nx-freebsd-x64": "20.3.0", - "@nx/nx-linux-arm-gnueabihf": "20.3.0", - "@nx/nx-linux-arm64-gnu": "20.3.0", - "@nx/nx-linux-arm64-musl": "20.3.0", - "@nx/nx-linux-x64-gnu": "20.3.0", - "@nx/nx-linux-x64-musl": "20.3.0", - "@nx/nx-win32-arm64-msvc": "20.3.0", - "@nx/nx-win32-x64-msvc": "20.3.0" + "@nx/nx-darwin-arm64": "20.3.1", + "@nx/nx-darwin-x64": "20.3.1", + "@nx/nx-freebsd-x64": "20.3.1", + "@nx/nx-linux-arm-gnueabihf": "20.3.1", + "@nx/nx-linux-arm64-gnu": "20.3.1", + "@nx/nx-linux-arm64-musl": "20.3.1", + "@nx/nx-linux-x64-gnu": "20.3.1", + "@nx/nx-linux-x64-musl": "20.3.1", + "@nx/nx-win32-arm64-msvc": "20.3.1", + "@nx/nx-win32-x64-msvc": "20.3.1" }, "peerDependencies": { "@swc-node/register": "^1.8.0", @@ -17406,6 +17984,7 @@ "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -17428,7 +18007,8 @@ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/on-finished": { "version": "2.4.1", @@ -17436,6 +18016,7 @@ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -17449,6 +18030,7 @@ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -17485,6 +18067,7 @@ "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", @@ -17579,7 +18162,8 @@ "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.3.tgz", "integrity": "sha512-oGFr3T+pYdTGJ+YFEILMpS3es+GiIbs9h/XQrclBXUtd44ey7XwfsMzM31f64I1SQOawDoDr/D823kNCADI8TA==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/os-tmpdir": { "version": "1.0.2", @@ -17624,16 +18208,13 @@ } }, "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", "dev": true, "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -17645,6 +18226,7 @@ "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/retry": "0.12.2", "is-network-error": "^1.0.0", @@ -17663,6 +18245,7 @@ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 4" } @@ -17685,35 +18268,35 @@ "license": "BlueOak-1.0.0" }, "node_modules/pacote": { - "version": "18.0.6", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", - "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-20.0.0.tgz", + "integrity": "sha512-pRjC5UFwZCgx9kUFDVM9YEahv4guZ1nSLqwmWiLUnDbGsjs+U5w7z6Uc8HNR1a6x8qnu5y9xtGE6D1uAuYz+0A==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/git": "^5.0.0", - "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/package-json": "^5.1.0", - "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^8.0.0", - "cacache": "^18.0.0", + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", - "npm-package-arg": "^11.0.0", - "npm-packlist": "^8.0.0", - "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^17.0.0", - "proc-log": "^4.0.0", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^9.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", "promise-retry": "^2.0.1", - "sigstore": "^2.2.0", - "ssri": "^10.0.0", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", "tar": "^6.1.11" }, "bin": { "pacote": "bin/index.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/pako": { @@ -17723,9 +18306,9 @@ "license": "(MIT AND Zlib)" }, "node_modules/papaparse": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", - "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.1.tgz", + "integrity": "sha512-EuEKUhyxrHVozD7g3/ztsJn6qaKse8RPfR6buNB2dMJvdtXNhcw8jccVi/LxNEY3HVrV6GO6Z4OoeCG9Iy9wpA==", "license": "MIT" }, "node_modules/parent-module": { @@ -17780,6 +18363,7 @@ "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.10" } @@ -17831,6 +18415,7 @@ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -17996,6 +18581,7 @@ "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=16" } @@ -18006,6 +18592,7 @@ "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -18105,6 +18692,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -18120,13 +18708,13 @@ } }, "node_modules/piscina": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.6.1.tgz", - "integrity": "sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.7.0.tgz", + "integrity": "sha512-b8hvkpp9zS0zsfa939b/jXbe64Z2gZv0Ha7FYPNUiDIB1y2AtxcOZdfP8xN8HFjUaqQiT9gRlfjAsoL8vdJ1Iw==", "dev": true, "license": "MIT", "optionalDependencies": { - "nice-napi": "^1.0.2" + "@napi-rs/nice": "^1.0.1" } }, "node_modules/pkg-dir": { @@ -18135,6 +18723,7 @@ "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "find-up": "^6.3.0" }, @@ -18151,6 +18740,7 @@ "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" @@ -18168,6 +18758,7 @@ "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-locate": "^6.0.0" }, @@ -18184,6 +18775,7 @@ "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "yocto-queue": "^1.0.0" }, @@ -18200,6 +18792,7 @@ "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "p-limit": "^4.0.0" }, @@ -18216,6 +18809,7 @@ "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } @@ -18226,6 +18820,7 @@ "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12.20" }, @@ -18268,6 +18863,7 @@ "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cosmiconfig": "^9.0.0", "jiti": "^1.20.0", @@ -18307,6 +18903,7 @@ "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": "^10 || ^12 || >= 14" }, @@ -18320,6 +18917,7 @@ "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^7.0.0", @@ -18338,6 +18936,7 @@ "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "postcss-selector-parser": "^7.0.0" }, @@ -18354,6 +18953,7 @@ "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "icss-utils": "^5.0.0" }, @@ -18370,6 +18970,7 @@ "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -18462,13 +19063,13 @@ "peer": true }, "node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/process-nextick-args": { @@ -18535,6 +19136,7 @@ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -18549,6 +19151,7 @@ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.10" } @@ -18566,7 +19169,8 @@ "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", "dev": true, "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/punycode": { "version": "2.3.1", @@ -18610,6 +19214,7 @@ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "side-channel": "^1.0.6" }, @@ -18652,6 +19257,7 @@ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -18662,6 +19268,7 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -18672,6 +19279,7 @@ "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -18688,6 +19296,7 @@ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -18847,29 +19456,16 @@ "license": "MIT" }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">= 14.16.0" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/redux": { @@ -18910,7 +19506,8 @@ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/regenerate-unicode-properties": { "version": "10.2.0", @@ -18918,6 +19515,7 @@ "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "regenerate": "^1.4.2" }, @@ -18937,6 +19535,7 @@ "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.8.4" } @@ -18946,7 +19545,8 @@ "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/regexpu-core": { "version": "6.2.0", @@ -18954,6 +19554,7 @@ "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", @@ -18971,7 +19572,8 @@ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/regjsparser": { "version": "0.12.0", @@ -18979,6 +19581,7 @@ "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "jsesc": "~3.0.2" }, @@ -18992,6 +19595,7 @@ "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "dev": true, "license": "MIT", + "peer": true, "bin": { "jsesc": "bin/jsesc" }, @@ -19081,6 +19685,7 @@ "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "adjust-sourcemap-loader": "^4.0.0", "convert-source-map": "^1.7.0", @@ -19098,6 +19703,7 @@ "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -19113,6 +19719,7 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -19208,9 +19815,9 @@ } }, "node_modules/rimraf/node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", "dev": true, "license": "ISC", "dependencies": { @@ -19291,13 +19898,13 @@ } }, "node_modules/rollup": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", - "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.26.0.tgz", + "integrity": "sha512-ilcl12hnWonG8f+NxU6BlgysVA0gvY2l8N0R84S1HcINbW20bvwuCngJkkInV6LXhwRpucsW5k1ovDwEdBVrNg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -19307,38 +19914,34 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.22.4", - "@rollup/rollup-android-arm64": "4.22.4", - "@rollup/rollup-darwin-arm64": "4.22.4", - "@rollup/rollup-darwin-x64": "4.22.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", - "@rollup/rollup-linux-arm-musleabihf": "4.22.4", - "@rollup/rollup-linux-arm64-gnu": "4.22.4", - "@rollup/rollup-linux-arm64-musl": "4.22.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", - "@rollup/rollup-linux-riscv64-gnu": "4.22.4", - "@rollup/rollup-linux-s390x-gnu": "4.22.4", - "@rollup/rollup-linux-x64-gnu": "4.22.4", - "@rollup/rollup-linux-x64-musl": "4.22.4", - "@rollup/rollup-win32-arm64-msvc": "4.22.4", - "@rollup/rollup-win32-ia32-msvc": "4.22.4", - "@rollup/rollup-win32-x64-msvc": "4.22.4", + "@rollup/rollup-android-arm-eabi": "4.26.0", + "@rollup/rollup-android-arm64": "4.26.0", + "@rollup/rollup-darwin-arm64": "4.26.0", + "@rollup/rollup-darwin-x64": "4.26.0", + "@rollup/rollup-freebsd-arm64": "4.26.0", + "@rollup/rollup-freebsd-x64": "4.26.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.26.0", + "@rollup/rollup-linux-arm-musleabihf": "4.26.0", + "@rollup/rollup-linux-arm64-gnu": "4.26.0", + "@rollup/rollup-linux-arm64-musl": "4.26.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.26.0", + "@rollup/rollup-linux-riscv64-gnu": "4.26.0", + "@rollup/rollup-linux-s390x-gnu": "4.26.0", + "@rollup/rollup-linux-x64-gnu": "4.26.0", + "@rollup/rollup-linux-x64-musl": "4.26.0", + "@rollup/rollup-win32-arm64-msvc": "4.26.0", + "@rollup/rollup-win32-ia32-msvc": "4.26.0", + "@rollup/rollup-win32-x64-msvc": "4.26.0", "fsevents": "~2.3.2" } }, - "node_modules/rollup/node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true, - "license": "MIT" - }, "node_modules/router": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/router/-/router-2.0.0.tgz", "integrity": "sha512-dIM5zVoG8xhC6rnSN8uoAgFARwTE7BQs8YwHEvK0VCmfxQXMaOuA1uiR1IPwsW7JyK5iTt7Od/TC9StasS2NPQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "array-flatten": "3.0.0", "is-promise": "4.0.0", @@ -19365,6 +19968,7 @@ "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -19439,11 +20043,12 @@ } }, "node_modules/sass-loader": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz", - "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==", + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.3.tgz", + "integrity": "sha512-gosNorT1RCkuCMyihv6FBRR7BMV06oKRAs+l4UMp1mlcVg9rWN6KMmUj3igjQwmYys4mDP3etEYJgiHRbgHCHA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "neo-async": "^2.6.2" }, @@ -19479,43 +20084,14 @@ } } }, - "node_modules/sass/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/sass/node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", "dev": true, "license": "ISC", - "optional": true + "optional": true, + "peer": true }, "node_modules/saxes": { "version": "6.0.0", @@ -19545,6 +20121,7 @@ "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -19565,6 +20142,7 @@ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -19582,7 +20160,8 @@ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/selfsigned": { "version": "2.4.1", @@ -19590,6 +20169,7 @@ "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node-forge": "^1.3.0", "node-forge": "^1" @@ -19616,6 +20196,7 @@ "integrity": "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.3.5", "destroy": "^1.2.0", @@ -19640,6 +20221,7 @@ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -19650,6 +20232,7 @@ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -19660,6 +20243,7 @@ "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "accepts": "~1.3.4", "batch": "0.6.1", @@ -19679,6 +20263,7 @@ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -19693,6 +20278,7 @@ "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -19703,6 +20289,7 @@ "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "depd": "~1.1.2", "inherits": "2.0.3", @@ -19718,7 +20305,8 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/serve-index/node_modules/negotiator": { "version": "0.6.3", @@ -19726,6 +20314,7 @@ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -19735,7 +20324,8 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/serve-index/node_modules/statuses": { "version": "1.5.0", @@ -19743,6 +20333,7 @@ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -19753,6 +20344,7 @@ "integrity": "sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -19803,7 +20395,8 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/shallow-clone": { "version": "3.0.1", @@ -19811,6 +20404,7 @@ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "kind-of": "^6.0.2" }, @@ -19853,6 +20447,7 @@ "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4" }, @@ -19866,6 +20461,7 @@ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -19886,6 +20482,7 @@ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -19903,6 +20500,7 @@ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -19922,6 +20520,7 @@ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -19957,21 +20556,21 @@ } }, "node_modules/sigstore": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", - "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-3.0.0.tgz", + "integrity": "sha512-PHMifhh3EN4loMcHCz6l3v/luzgT3za+9f8subGgeMNjbJjzH4Ij/YoX3Gvu+kaouJRIlVdTHHCREADYf+ZteA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.0.0", + "@sigstore/bundle": "^3.0.0", + "@sigstore/core": "^2.0.0", "@sigstore/protobuf-specs": "^0.3.2", - "@sigstore/sign": "^2.3.2", - "@sigstore/tuf": "^2.3.4", - "@sigstore/verify": "^1.2.1" + "@sigstore/sign": "^3.0.0", + "@sigstore/tuf": "^3.0.0", + "@sigstore/verify": "^2.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/simple-statistics": { @@ -19996,6 +20595,7 @@ "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=14.16" }, @@ -20056,6 +20656,7 @@ "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "faye-websocket": "^0.11.3", "uuid": "^8.3.2", @@ -20087,6 +20688,7 @@ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "uuid": "dist/bin/uuid" } @@ -20147,6 +20749,7 @@ "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "iconv-lite": "^0.6.3", "source-map-js": "^1.0.2" @@ -20168,6 +20771,7 @@ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -20181,6 +20785,7 @@ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -20192,6 +20797,7 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -20238,6 +20844,7 @@ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.1.0", "handle-thing": "^2.0.0", @@ -20255,6 +20862,7 @@ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "debug": "^4.1.0", "detect-node": "^2.0.4", @@ -20270,6 +20878,7 @@ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -20293,16 +20902,16 @@ "license": "BSD-3-Clause" }, "node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", + "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/stack-utils": { @@ -20341,6 +20950,7 @@ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -20641,6 +21251,7 @@ "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -20731,6 +21342,33 @@ "node": ">=8" } }, + "node_modules/tar/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -20739,11 +21377,12 @@ "license": "ISC" }, "node_modules/terser": { - "version": "5.31.6", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", - "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", + "integrity": "sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -20763,6 +21402,7 @@ "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -20798,6 +21438,7 @@ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -20813,6 +21454,7 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -20828,7 +21470,8 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/test-exclude": { "version": "6.0.0", @@ -20875,6 +21518,7 @@ "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", "dev": true, "license": "Unlicense", + "peer": true, "engines": { "node": ">=10.18" }, @@ -20887,7 +21531,8 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tinybench": { "version": "2.9.0", @@ -20950,22 +21595,22 @@ } }, "node_modules/tldts": { - "version": "6.1.70", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.70.tgz", - "integrity": "sha512-/W1YVgYVJd9ZDjey5NXadNh0mJXkiUMUue9Zebd0vpdo1sU+H4zFFTaJ1RKD4N6KFoHfcXy6l+Vu7bh+bdWCzA==", + "version": "6.1.71", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.71.tgz", + "integrity": "sha512-LQIHmHnuzfZgZWAf2HzL83TIIrD8NhhI0DVxqo9/FdOd4ilec+NTNZOlDZf7EwrTNoutccbsHjvWHYXLAtvxjw==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^6.1.70" + "tldts-core": "^6.1.71" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.70", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.70.tgz", - "integrity": "sha512-RNnIXDB1FD4T9cpQRErEqw6ZpjLlGdMOitdV+0xtbsnwr4YFka1zpc7D4KD+aAn8oSG5JyFrdasZTE04qDE9Yg==", + "version": "6.1.71", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.71.tgz", + "integrity": "sha512-LRbChn2YRpic1KxY+ldL1pGXN/oVvKfCVufwfVzEQdFYNo39uF7AJa/WXdo+gYO7PTvdfkCPCed6Hkvz/kR7jg==", "dev": true, "license": "MIT" }, @@ -21007,6 +21652,7 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.6" } @@ -21052,6 +21698,7 @@ "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=10.0" }, @@ -21069,6 +21716,7 @@ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, "license": "MIT", + "peer": true, "bin": { "tree-kill": "cli.js" } @@ -21088,16 +21736,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", + "integrity": "sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-cacheable": { @@ -21289,18 +21937,18 @@ "license": "0BSD" }, "node_modules/tuf-js": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", - "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-3.0.1.tgz", + "integrity": "sha512-+68OP1ZzSF84rTckf3FA95vJ1Zlx/uaXyiiKyPd1pA4rZNkpEvDAKmsu1xUSmbF/chCRYgZ6UZkDwC7PmzmAyA==", "dev": true, "license": "MIT", "dependencies": { - "@tufjs/models": "2.0.1", - "debug": "^4.3.4", - "make-fetch-happen": "^13.0.1" + "@tufjs/models": "3.0.1", + "debug": "^4.3.6", + "make-fetch-happen": "^14.0.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/turndown": { @@ -21354,6 +22002,7 @@ "integrity": "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -21369,6 +22018,7 @@ "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.6" } @@ -21379,6 +22029,7 @@ "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mime-db": "^1.53.0" }, @@ -21391,12 +22042,13 @@ "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -21416,15 +22068,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.0.tgz", - "integrity": "sha512-Ni8sUkVWYK4KAcTtPjQ/UTiRk6jcsuDhPpxULapUDi8A/l8TSBk+t1GtJA1RsCzIJg0q6+J7bf35AwQigENWRQ==", + "version": "8.19.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.19.1.tgz", + "integrity": "sha512-LKPUQpdEMVOeKluHi8md7rwLcoXHhwvWp3x+sJkMuq3gGm9yaYJtPo8sRZSblMFJ5pcOGCAak/scKf1mvZDlQw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.19.0", - "@typescript-eslint/parser": "8.19.0", - "@typescript-eslint/utils": "8.19.0" + "@typescript-eslint/eslint-plugin": "8.19.1", + "@typescript-eslint/parser": "8.19.1", + "@typescript-eslint/utils": "8.19.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -21472,6 +22124,7 @@ "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -21482,6 +22135,7 @@ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -21496,6 +22150,7 @@ "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -21506,6 +22161,7 @@ "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=4" } @@ -21516,6 +22172,7 @@ "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -21524,29 +22181,29 @@ } }, "node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", + "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==", "dev": true, "license": "ISC", "dependencies": { - "unique-slug": "^4.0.0" + "unique-slug": "^5.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz", + "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==", "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/unist-util-stringify-position": { @@ -21579,14 +22236,15 @@ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", "funding": [ { "type": "opencollective", @@ -21604,7 +22262,7 @@ "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -21654,14 +22312,15 @@ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4.0" } }, "node_modules/uuid": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.4.tgz", - "integrity": "sha512-IzL6VtTTYcAhA/oghbFJ1Dkmqev+FpQWnCBaKq/gUluLxliWvO8DPFWfIviRmYbtaavtSQe4WBL++rFjdcGWEg==", + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -21712,13 +22371,13 @@ } }, "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.0.tgz", + "integrity": "sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/vary": { @@ -21727,6 +22386,7 @@ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.8" } @@ -22360,16 +23020,6 @@ } } }, - "node_modules/vitest/node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -22394,9 +23044,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", - "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, "license": "MIT", "dependencies": { @@ -22413,6 +23063,7 @@ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "minimalistic-assert": "^1.0.0" } @@ -22432,7 +23083,8 @@ "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/weak-napi": { "version": "2.0.2", @@ -22520,19 +23172,20 @@ } }, "node_modules/webpack": { - "version": "5.94.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", - "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "version": "5.96.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.96.1.tgz", + "integrity": "sha512-l2LlBSvVZGhL4ZrPwyr8+37AunkcYj5qh8o6u2/2rzoPc8gxFJkLj1WxNgooi9pnoc06jh0BjuXnamM4qlujZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "@types/estree": "^1.0.5", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", @@ -22572,6 +23225,7 @@ "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "colorette": "^2.0.10", "memfs": "^4.6.0", @@ -22602,6 +23256,7 @@ "integrity": "sha512-90SqqYXA2SK36KcT6o1bvwvZfJFcmoamqeJY7+boioffX9g9C0wjjJRGUrQIuh43pb0ttX7+ssavmj/WN2RHtA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", @@ -22653,12 +23308,53 @@ } } }, + "node_modules/webpack-dev-server/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/webpack-dev-server/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", @@ -22678,12 +23374,41 @@ } } }, + "node_modules/webpack-dev-server/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/webpack-dev-server/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/webpack-merge": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", @@ -22699,6 +23424,7 @@ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10.13.0" } @@ -22709,6 +23435,7 @@ "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "typed-assert": "^1.0.8" }, @@ -22731,6 +23458,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -22748,6 +23476,7 @@ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, "license": "MIT", + "peer": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -22758,6 +23487,7 @@ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -22772,6 +23502,7 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "engines": { "node": ">=4.0" } @@ -22781,14 +23512,16 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/webpack/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.3.0", @@ -22796,6 +23529,7 @@ "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -22926,7 +23660,8 @@ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/word-wrap": { "version": "1.2.5", @@ -23243,9 +23978,9 @@ } }, "node_modules/zone.js": { - "version": "0.14.10", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", - "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.0.tgz", + "integrity": "sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==", "license": "MIT" } } diff --git a/package.json b/package.json index d392de262aa4..0fa24135ef47 100644 --- a/package.json +++ b/package.json @@ -13,30 +13,30 @@ "node_modules" ], "dependencies": { - "@angular/animations": "18.2.13", - "@angular/cdk": "18.2.14", - "@angular/common": "18.2.13", - "@angular/compiler": "18.2.13", - "@angular/core": "18.2.13", - "@angular/forms": "18.2.13", - "@angular/localize": "18.2.13", - "@angular/material": "18.2.14", - "@angular/platform-browser": "18.2.13", - "@angular/platform-browser-dynamic": "18.2.13", - "@angular/router": "18.2.13", - "@angular/service-worker": "18.2.13", + "@angular/animations": "19.0.6", + "@angular/cdk": "19.0.5", + "@angular/common": "19.0.6", + "@angular/compiler": "19.0.6", + "@angular/core": "19.0.6", + "@angular/forms": "19.0.6", + "@angular/localize": "19.0.6", + "@angular/material": "19.0.5", + "@angular/platform-browser": "19.0.6", + "@angular/platform-browser-dynamic": "19.0.6", + "@angular/router": "19.0.6", + "@angular/service-worker": "19.0.6", "@ctrl/ngx-emoji-mart": "9.2.0", - "@danielmoncada/angular-datetime-picker": "18.1.0", + "@danielmoncada/angular-datetime-picker": "19.0.0", "@fingerprintjs/fingerprintjs": "4.5.1", - "@fortawesome/angular-fontawesome": "0.15.0", + "@fortawesome/angular-fontawesome": "1.0.0", "@fortawesome/fontawesome-svg-core": "6.7.2", "@fortawesome/free-regular-svg-icons": "6.7.2", "@fortawesome/free-solid-svg-icons": "6.7.2", "@ls1intum/apollon": "3.3.15", - "@ng-bootstrap/ng-bootstrap": "17.0.1", + "@ng-bootstrap/ng-bootstrap": "18.0.0", "@ngx-translate/core": "16.0.4", "@ngx-translate/http-loader": "16.0.1", - "@sentry/angular": "8.47.0", + "@sentry/angular": "8.48.0", "@siemens/ngx-datatable": "22.4.1", "@swimlane/ngx-charts": "21.1.2", "@swimlane/ngx-graph": "9.0.1", @@ -44,7 +44,7 @@ "@vscode/markdown-it-katex": "1.1.1", "bootstrap": "5.3.3", "compare-versions": "6.1.1", - "core-js": "3.39.0", + "core-js": "3.40.0", "crypto-js": "4.2.0", "dayjs": "1.11.13", "diff-match-patch-typescript": "1.1.0", @@ -64,9 +64,9 @@ "markdown-it-highlightjs": "4.2.0", "mobile-drag-drop": "3.0.0-rc.0", "monaco-editor": "0.52.2", - "ngx-infinite-scroll": "18.0.0", - "ngx-webstorage": "18.0.0", - "papaparse": "5.4.1", + "ngx-infinite-scroll": "19.0.0", + "ngx-webstorage": "19.0.1", + "papaparse": "5.5.1", "pdf-lib": "1.17.1", "pdfjs-dist": "4.10.38", "rxjs": "7.8.1", @@ -77,24 +77,51 @@ "ts-cacheable": "1.0.10", "tslib": "2.8.1", "turndown": "7.2.0", - "uuid": "11.0.4", + "uuid": "11.0.5", "webstomp-client": "1.2.6", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", - "zone.js": "0.14.10" + "zone.js": "0.15.0" }, "overrides": { + "@angular-builders/jest": { + "@angular-devkit/build-angular": "^19.0.0", + "@angular/compiler-cli": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/platform-browser-dynamic": "^19.0.0" + }, + "@swimlane/ngx-charts": { + "@angular/animations": "^19.0.0", + "@angular/cdk": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/common": "^19.0.0", + "@angular/forms": "^19.0.0", + "@angular/platform-browser": "^19.0.0", + "@angular/platform-browser-dynamic": "^19.0.0" + }, + "@swimlane/ngx-graph": { + "@angular/animations": "^19.0.0", + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/cdk": "^19.0.0" + }, "@typescript-eslint/utils": { - "eslint": "^9.17.0" + "eslint": "^9.18.0" }, "braces": "3.0.3", "cookie": "1.0.2", "critters": "0.0.25", "debug": "4.4.0", "eslint-plugin-deprecation": { - "eslint": "^9.17.0" + "eslint": "^9.18.0" }, "express": "5.0.1", "jsdom": "25.0.1", + "ng-mocks": { + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/forms": "^19.0.0", + "@angular/platform-browser": "^19.0.0" + }, "postcss": "8.4.49", "rimraf": "6.0.1", "semver": "7.6.3", @@ -107,21 +134,21 @@ "yargs-parser": "21.1.1" }, "devDependencies": { - "@analogjs/vite-plugin-angular": "1.11.0", - "@angular-builders/jest": "18.0.0", - "@angular-devkit/build-angular": "18.2.12", - "@angular-eslint/builder": "18.4.3", - "@angular-eslint/eslint-plugin": "18.4.3", - "@angular-eslint/eslint-plugin-template": "18.4.3", - "@angular-eslint/schematics": "18.4.3", - "@angular-eslint/template-parser": "18.4.3", - "@angular/cli": "18.2.12", - "@angular/compiler-cli": "18.2.13", - "@angular/language-service": "18.2.13", - "@sentry/types": "8.47.0", + "@analogjs/vite-plugin-angular": "1.12.0", + "@angular-builders/jest": "19.0.0", + "@angular-eslint/builder": "19.0.2", + "@angular-eslint/eslint-plugin": "19.0.2", + "@angular-eslint/eslint-plugin-template": "19.0.2", + "@angular-eslint/schematics": "19.0.2", + "@angular-eslint/template-parser": "19.0.2", + "@angular/build": "19.0.7", + "@angular/cli": "19.0.7", + "@angular/compiler-cli": "19.0.6", + "@angular/language-service": "19.0.6", + "@sentry/types": "8.48.0", "@testing-library/angular": "17.3.5", "@types/crypto-js": "4.2.2", - "@types/d3-shape": "3.1.6", + "@types/d3-shape": "3.1.7", "@types/dompurify": "3.0.5", "@types/emoji-js": "3.5.2", "@types/jest": "29.5.14", @@ -133,15 +160,15 @@ "@types/sockjs-client": "1.5.4", "@types/turndown": "5.0.5", "@types/uuid": "10.0.0", - "@typescript-eslint/eslint-plugin": "8.19.0", - "@typescript-eslint/parser": "8.19.0", - "eslint": "9.17.0", + "@typescript-eslint/eslint-plugin": "8.19.1", + "@typescript-eslint/parser": "8.19.1", + "eslint": "9.18.0", "eslint-config-prettier": "9.1.0", "eslint-plugin-deprecation": "3.0.0", "eslint-plugin-jest": "28.10.0", "eslint-plugin-jest-extended": "2.4.0", "eslint-plugin-prettier": "5.2.1", - "folder-hash": "4.1.0", + "folder-hash": "4.1.1", "husky": "9.1.7", "jest": "29.7.0", "jest-canvas-mock": "2.5.2", @@ -149,7 +176,7 @@ "jest-extended": "4.0.2", "jest-fail-on-console": "3.3.1", "jest-junit": "16.0.0", - "jest-preset-angular": "14.4.2", + "jest-preset-angular": "14.5.0", "lint-staged": "15.3.0", "ng-mocks": "14.13.1", "ngxtension": "4.2.0", @@ -158,8 +185,8 @@ "rimraf": "6.0.1", "sass": "1.83.1", "ts-jest": "29.2.5", - "typescript": "5.5.4", - "typescript-eslint": "8.19.0", + "typescript": "5.6.3", + "typescript-eslint": "8.19.1", "vite-tsconfig-paths": "5.1.4", "vitest": "2.1.8", "weak-napi": "2.0.2" @@ -190,6 +217,6 @@ "testw8": "npm run prebuild && ng test --coverage --log-heap-usage -w=8", "update": "ncu -i --format group", "webapp:build": "npm run clean-www && npm run prebuild -- --develop && ng build --configuration development", - "webapp:prod": "npm run clean-www && npm run prebuild && ng build --configuration production" + "webapp:prod": "npm run clean-www && npm run prebuild && NG_BUILD_OPTIMIZE_CHUNKS=1 ng build --configuration production" } } diff --git a/patches/ng-mocks+14.13.1.patch b/patches/ng-mocks+14.13.1.patch new file mode 100644 index 000000000000..2a370550a707 --- /dev/null +++ b/patches/ng-mocks+14.13.1.patch @@ -0,0 +1,16 @@ +diff --git a/node_modules/ng-mocks/index.js b/node_modules/ng-mocks/index.js +index e49d71d..049e8ed 100644 +--- a/node_modules/ng-mocks/index.js ++++ b/node_modules/ng-mocks/index.js +@@ -1,2 +1,2 @@ +-!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("@angular/core"),require("@angular/core/testing"),require("@angular/forms"),require("@angular/common"),require("@angular/platform-browser"));else if("function"==typeof define&&define.amd)define(["@angular/core","@angular/core/testing","@angular/forms","@angular/common","@angular/platform-browser"],t);else{var r="object"==typeof exports?t(require("@angular/core"),require("@angular/core/testing"),require("@angular/forms"),require("@angular/common"),require("@angular/platform-browser")):t(e["@angular/core"],e["@angular/core/testing"],e["@angular/forms"],e["@angular/common"],e["@angular/platform-browser"]);for(var n in r)("object"==typeof exports?exports:e)[n]=r[n]}}(this,(function(__WEBPACK_EXTERNAL_MODULE__860__,__WEBPACK_EXTERNAL_MODULE__2603__,__WEBPACK_EXTERNAL_MODULE__7182__,__WEBPACK_EXTERNAL_MODULE__4358__,__WEBPACK_EXTERNAL_MODULE__1165__){return function(){"use strict";var __webpack_modules__={5170:function(e,t,r){var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,o)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),o=this&&this.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||n(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),t.MockRenderFactory=t.MockRender=t.ngMocks=t.MockService=t.MockProviders=t.MockProvider=t.MockDeclarations=t.MockDeclaration=t.MockedPipe=t.MockPipes=t.MockPipe=t.MockedDirective=t.MockDirectives=t.MockDirective=t.MockedComponent=t.MockComponents=t.MockComponent=t.MockedModule=t.MockModule=t.IMockBuilderProvider=t.IMockBuilderResult=t.IMockBuilderConfigModule=t.IMockBuilderConfigDirective=t.IMockBuilderConfigComponent=t.IMockBuilderConfigAll=t.IMockBuilderConfig=t.IMockBuilderExtended=t.IMockBuilder=t.MockBuilder=t.MockReset=t.MockInstance=t.LegacyControlValueAccessor=t.MockValidator=t.MockControlValueAccessor=t.Mock=t.isNgInjectionToken=t.isNgDef=t.isMockedNgDefOf=t.isMockValidator=t.isMockOf=t.isMockNgDef=t.isMockControlValueAccessor=t.getSourceOfMock=t.getMockedNgDefOf=t.getInjection=t.getTestBedInjection=void 0,r(7592),r(4081),o(r(1763),t);var a=r(6456);Object.defineProperty(t,"getTestBedInjection",{enumerable:!0,get:function(){return a.getTestBedInjection}}),Object.defineProperty(t,"getInjection",{enumerable:!0,get:function(){return a.getInjection}});var i=r(5590);Object.defineProperty(t,"getMockedNgDefOf",{enumerable:!0,get:function(){return i.getMockedNgDefOf}});var l=r(6739);Object.defineProperty(t,"getSourceOfMock",{enumerable:!0,get:function(){return l.getSourceOfMock}});var u=r(7105);Object.defineProperty(t,"isMockControlValueAccessor",{enumerable:!0,get:function(){return u.isMockControlValueAccessor}});var f=r(6763);Object.defineProperty(t,"isMockNgDef",{enumerable:!0,get:function(){return f.isMockNgDef}});var c=r(7675);Object.defineProperty(t,"isMockOf",{enumerable:!0,get:function(){return c.isMockOf}});var d=r(2650);Object.defineProperty(t,"isMockValidator",{enumerable:!0,get:function(){return d.isMockValidator}});var s=r(732);Object.defineProperty(t,"isMockedNgDefOf",{enumerable:!0,get:function(){return s.isMockedNgDefOf}});var v=r(5974);Object.defineProperty(t,"isNgDef",{enumerable:!0,get:function(){return v.isNgDef}});var p=r(4152);Object.defineProperty(t,"isNgInjectionToken",{enumerable:!0,get:function(){return p.isNgInjectionToken}});var h=r(1946);Object.defineProperty(t,"Mock",{enumerable:!0,get:function(){return h.Mock}});var y=r(5980);Object.defineProperty(t,"MockControlValueAccessor",{enumerable:!0,get:function(){return y.MockControlValueAccessor}}),Object.defineProperty(t,"MockValidator",{enumerable:!0,get:function(){return y.MockValidator}}),Object.defineProperty(t,"LegacyControlValueAccessor",{enumerable:!0,get:function(){return y.LegacyControlValueAccessor}});var _=r(3001);Object.defineProperty(t,"MockInstance",{enumerable:!0,get:function(){return _.MockInstance}}),Object.defineProperty(t,"MockReset",{enumerable:!0,get:function(){return _.MockReset}});var g=r(3771);Object.defineProperty(t,"MockBuilder",{enumerable:!0,get:function(){return g.MockBuilder}});var b=r(2938);Object.defineProperty(t,"IMockBuilder",{enumerable:!0,get:function(){return b.IMockBuilder}}),Object.defineProperty(t,"IMockBuilderExtended",{enumerable:!0,get:function(){return b.IMockBuilderExtended}}),Object.defineProperty(t,"IMockBuilderConfig",{enumerable:!0,get:function(){return b.IMockBuilderConfig}}),Object.defineProperty(t,"IMockBuilderConfigAll",{enumerable:!0,get:function(){return b.IMockBuilderConfigAll}}),Object.defineProperty(t,"IMockBuilderConfigComponent",{enumerable:!0,get:function(){return b.IMockBuilderConfigComponent}}),Object.defineProperty(t,"IMockBuilderConfigDirective",{enumerable:!0,get:function(){return b.IMockBuilderConfigDirective}}),Object.defineProperty(t,"IMockBuilderConfigModule",{enumerable:!0,get:function(){return b.IMockBuilderConfigModule}}),Object.defineProperty(t,"IMockBuilderResult",{enumerable:!0,get:function(){return b.IMockBuilderResult}}),Object.defineProperty(t,"IMockBuilderProvider",{enumerable:!0,get:function(){return b.IMockBuilderProvider}});var m=r(3821);Object.defineProperty(t,"MockModule",{enumerable:!0,get:function(){return m.MockModule}});var M=r(3877);Object.defineProperty(t,"MockedModule",{enumerable:!0,get:function(){return M.MockedModule}});var k=r(5395);Object.defineProperty(t,"MockComponent",{enumerable:!0,get:function(){return k.MockComponent}}),Object.defineProperty(t,"MockComponents",{enumerable:!0,get:function(){return k.MockComponents}});var w=r(2350);Object.defineProperty(t,"MockedComponent",{enumerable:!0,get:function(){return w.MockedComponent}});var O=r(8339);Object.defineProperty(t,"MockDirective",{enumerable:!0,get:function(){return O.MockDirective}}),Object.defineProperty(t,"MockDirectives",{enumerable:!0,get:function(){return O.MockDirectives}});var x=r(4748);Object.defineProperty(t,"MockedDirective",{enumerable:!0,get:function(){return x.MockedDirective}});var j=r(2937);Object.defineProperty(t,"MockPipe",{enumerable:!0,get:function(){return j.MockPipe}}),Object.defineProperty(t,"MockPipes",{enumerable:!0,get:function(){return j.MockPipes}});var D=r(5861);Object.defineProperty(t,"MockedPipe",{enumerable:!0,get:function(){return D.MockedPipe}});var S=r(5269);Object.defineProperty(t,"MockDeclaration",{enumerable:!0,get:function(){return S.MockDeclaration}}),Object.defineProperty(t,"MockDeclarations",{enumerable:!0,get:function(){return S.MockDeclarations}});var P=r(7413);Object.defineProperty(t,"MockProvider",{enumerable:!0,get:function(){return P.MockProvider}}),Object.defineProperty(t,"MockProviders",{enumerable:!0,get:function(){return P.MockProviders}});var C=r(5779);Object.defineProperty(t,"MockService",{enumerable:!0,get:function(){return C.MockService}});var N=r(6189);Object.defineProperty(t,"ngMocks",{enumerable:!0,get:function(){return N.ngMocks}});var T=r(1661);Object.defineProperty(t,"MockRender",{enumerable:!0,get:function(){return T.MockRender}});var E=r(8406);Object.defineProperty(t,"MockRenderFactory",{enumerable:!0,get:function(){return E.MockRenderFactory}}),o(r(7325),t),o(r(534),t)},3295:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default={flags:["cacheModule","cacheComponent","cacheDirective","cacheProvider","correctModuleExports"],mockRenderCacheSize:25,neverMockModule:["ApplicationModule","CommonModule","BrowserModule","_ApplicationModule","_CommonModule","_BrowserModule"],neverMockProvidedFunction:["DomRendererFactory2","EventManager","Injector","RendererFactory2","Sanitizer","DomSanitizer","DomSanitizerImpl","ApplicationInitStatus","ApplicationRef","Compiler","IterableDiffers","KeyValueDiffers","_DomRendererFactory2","_EventManager","_Injector","_Sanitizer","_DomSanitizer","_DomSanitizerImpl","_ApplicationInitStatus","_ApplicationRef","_Compiler","_IterableDiffers","_KeyValueDiffers"],neverMockToken:["InjectionToken Set Injector scope.","InjectionToken EventManagerPlugins","InjectionToken HammerGestureConfig","InjectionToken AppId","InjectionToken DefaultCurrencyCode","InjectionToken LocaleId","InjectionToken SCHEDULER_TOKEN"],onMockBuilderMissingDependency:"throw",onMockInstanceRestoreNeed:"warn",onTestBedFlushNeed:"warn",dependencies:["declarations","hostDirectives","entryComponents","bootstrap","providers","viewProviders","imports","exports"]}},5551:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=function(){function e(){this.stack=[],this.push()}return e.prototype.push=function(){this.stack.push(new Map)},e.prototype.pop=function(){var e;return null!==(e=this.stack.pop())&&void 0!==e?e:new Map},e.prototype.has=function(e){for(var t=this.stack.length-1;t>=0;t-=1)if(this.stack[t].has(e))return!0;return!1},e.prototype.get=function(e){for(var t=this.stack.length-1;t>=0;t-=1)if(this.stack[t].has(e))return this.stack[t].get(e)},e.prototype.set=function(e,t){for(var r=this.stack.length-1;r>=0;r-=1)this.stack[r].set(e,t);return this},e.prototype.merge=function(e){var t,r;try{for(var i=n((0,a.mapEntries)(e)),l=i.next();!l.done;l=i.next()){var u=o(l.value,2),f=u[0],c=u[1];this.set(f,c)}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}return this},e}();t.default=i},3174:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(445));t.default=function(e,t,r,n){void 0===n&&(n=!1),o.default.definePropertyDescriptor(e,t,{configurable:!0,enumerable:n,value:r,writable:!0})}},2023:function(e,t,r){var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,o)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),a=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&n(t,e,r);return o(t,e),t};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(7182)),l=i.AbstractControl,u=i.DefaultValueAccessor,f=i.FormControl,c=i.FormControlDirective,d=i.NG_ASYNC_VALIDATORS,s=i.NG_VALIDATORS,v=i.NG_VALUE_ACCESSOR,p=i.NgControl,h=i.NgModel;t.default={AbstractControl:l,DefaultValueAccessor:u,FormControl:f,FormControlDirective:c,NG_ASYNC_VALIDATORS:d,NG_VALIDATORS:s,NG_VALUE_ACCESSOR:v,NgControl:p,NgModel:h}},6456:function(__unused_webpack_module,exports,__webpack_require__){var __extends=this&&this.__extends||(extendStatics=function(e,t){return extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},extendStatics(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}extendStatics(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),extendStatics,__values=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},__read=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},__spreadArray=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o0&&(0,core_define_property_1.default)(t,"parameters",__spreadArray([],__read(r),!1)),t};exports.extendClass=extendClass},4201:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(6456),o={};t.default=function(e,t){if(void 0===t&&(t=o),t===o)return(0,n.getTestBedInjection)(e);try{return t.get(e)}catch(e){return}}},4874:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8641)),a=n(r(1585)),i=n(r(2129));t.default=function(e){return function(t){(0,o.default)(t);try{return e(t)}catch(e){(0,a.default)(t),(0,i.default)(t)}}}},8862:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4673)),a=n(r(4874));t.default=function(e){return(0,a.default)((function(e){var t=(0,o.default)(e);if(t.Component)return t.Component;if(t.Directive)return t.Directive;throw new Error("Cannot resolve declarations")}))(e)}},1381:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8862)),a=n(r(1345)),i=r(5974);t.default=function(e){return(0,i.isNgDef)(e,"c")||(0,i.isNgDef)(e,"d")?(0,o.default)(e):(0,i.isNgDef)(e,"m")?(0,a.default)(e):void 0}},1345:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4673)),a=n(r(4874));t.default=function(e){return(0,a.default)((function(e){var t=(0,o.default)(e);if(t.NgModule)return t.NgModule;throw new Error("Cannot resolve declarations")}))(e)}},8749:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4673));t.default=function(e){var t;return null!==(t=(0,o.default)(e).parameters)&&void 0!==t?t:[]}},8773:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4673)),a=n(r(4874));t.default=function(e){return(0,a.default)((function(e){var t=(0,o.default)(e);if(t.Pipe)return t.Pipe;throw new Error("Cannot resolve declarations")}))(e)}},8911:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t,r,n;if(e&&("object"==typeof e||"function"==typeof e))return null!==(r=null===(t=e.ɵprov)||void 0===t?void 0:t.providedIn)&&void 0!==r?r:null===(n=e.ngInjectableDef)||void 0===n?void 0:n.providedIn}},1763:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.NG_MOCKS_ROOT_PROVIDERS=t.NG_MOCKS_INTERCEPTORS=t.NG_MOCKS_RESOLVERS=t.NG_MOCKS_GUARDS=t.NG_MOCKS_OVERRIDES=t.NG_MOCKS_TOUCHES=t.NG_MOCKS=void 0;var n=r(860);t.NG_MOCKS=new n.InjectionToken("NG_MOCKS"),t.NG_MOCKS.__ngMocksSkip=!0,t.NG_MOCKS_TOUCHES=new n.InjectionToken("NG_MOCKS_TOUCHES"),t.NG_MOCKS_TOUCHES.__ngMocksSkip=!0,t.NG_MOCKS_OVERRIDES=new n.InjectionToken("NG_MOCKS_OVERRIDES"),t.NG_MOCKS_OVERRIDES.__ngMocksSkip=!0,t.NG_MOCKS_GUARDS=new n.InjectionToken("NG_MOCKS_GUARDS"),t.NG_MOCKS_GUARDS.__ngMocksSkip=!0,t.NG_MOCKS_RESOLVERS=new n.InjectionToken("NG_MOCKS_RESOLVERS"),t.NG_MOCKS_RESOLVERS.__ngMocksSkip=!0,t.NG_MOCKS_INTERCEPTORS=new n.InjectionToken("NG_MOCKS_INTERCEPTORS"),t.NG_MOCKS_INTERCEPTORS.__ngMocksSkip=!0,t.NG_MOCKS_ROOT_PROVIDERS=new n.InjectionToken("NG_MOCKS_ROOT_PROVIDERS"),t.NG_MOCKS_ROOT_PROVIDERS.__ngMocksSkip=!0},5756:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(860),i=o(r(6439)),l=o(r(1184));t.default=function(e,t,r){var o,u;if(t)try{for(var f=n(t),c=f.next();!c.done;c=f.next()){var d=c.value,s=(0,l.default)(d),v=s.name,p=s.alias,h=s.required;r&&-1!==r.indexOf(v)||(0,a.Input)((0,i.default)({name:v,alias:p,required:h},!0))(e.prototype,v)}}catch(e){o={error:e}}finally{try{c&&!c.done&&(u=f.return)&&u.call(f)}finally{if(o)throw o.error}}}},615:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(860),i=o(r(6439)),l=o(r(1184));t.default=function(e,t){var r,o;if(t)try{for(var u=n(t),f=u.next();!f.done;f=u.next()){var c=f.value,d=(0,l.default)(c),s=d.name,v=d.alias,p=d.required;(0,a.Output)((0,i.default)({name:s,alias:v,required:p},!0))(e.prototype,s)}}catch(e){r={error:e}}finally{try{f&&!f.done&&(o=u.return)&&o.call(u)}finally{if(r)throw r.error}}}},8385:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0});var i=r(860),l={ContentChild:i.ContentChild,ContentChildren:i.ContentChildren,ViewChild:i.ViewChild,ViewChildren:i.ViewChildren},u=function(e){return 0===e.indexOf("__mock")},f=function(e){return n(n({},e),{ngMetadataName:e.ngMetadataName,read:i.ViewContainerRef})};t.default=function(e,t){var r,n;if(!t)return[];var i=a(function(e){var t,r,n=[],a=[];try{for(var i=o(Object.keys(e)),l=i.next();!l.done;l=i.next()){var c=l.value,d=e[c];n.push([c,d]),d.isViewQuery||u(c)||(a.push(c),n.push(["__ngMocksVcr_".concat(c),f(d)]))}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}return[n,a]}(t),2),c=i[0],d=i[1];try{for(var s=o(c),v=s.next();!v.done;v=s.next()){var p=a(v.value,2),h=p[0],y=p[1];y.ngMetadataName&&(0,l[y.ngMetadataName])(y.selector,y)(e.prototype,h)}}catch(e){r={error:e}}finally{try{v&&!v.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}return d}},8641:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){if(!e)throw new Error(["undefined / null has been passed into ng-mocks as a declaration / provider.","Please ensure that the current test file has correct imports:","imported files exist and imported declarations have been exported in the file."].join(" "))}},1585:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(2970)),a=n(r(9628));t.default=function(e){if((0,a.default)(e))throw new Error(["ng-mocks got ".concat((0,o.default)(e)," which has been already mocked by jest.mock()."),"It is not possible to produce correct mocks for it, because jest.mock() removes Angular decorators.","To fix this, please avoid jest.mock() on the file which exports ".concat((0,o.default)(e)," or add jest.dontMock() on it."),"The same should be done for all related dependencies."].join(" "))}},2129:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(2970));t.default=function(e){throw new Error(["".concat((0,o.default)(e)," declaration has been passed into ng-mocks without Angular decorators."),"Therefore, it cannot be properly handled.","Highly likely,","undefined"==typeof jest?"":"jest.mock() has been used on its file, or","ng-mocks is imported in production code, or got a class without Angular decoration.","Otherwise, please create an issue on github: https://github.com/help-me-mom/ng-mocks/issues/new?title=False%20positive%20ng-mocks%20not%20in%20JIT.","Thank you in advance for support."].join(" "))}},6439:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){var r=e.name,n=e.alias,o=e.required;return void 0===t&&(t=!1),o?{name:r,alias:n,required:o}:n&&r!==n?t?n:"".concat(r,":").concat(n):t?"":r}},1184:function(e,t){var r=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){if("string"==typeof e){var t=r(e.split(":").map((function(e){return e.trim()})),2),n=t[0],o=t[1];return n!==o&&o?{name:n,alias:o}:{name:n}}return e}},5204:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.funcExtractDeps=void 0;var a=o(r(4673)),i=o(r(3295)),l=r(6456),u=r(9335),f=o(r(6297));t.funcExtractDeps=function(e,r,o){var c,d,s,v;void 0===o&&(o=!1);var p=(0,a.default)(e),h=(0,u.getNgType)(e);if(!h||"Injectable"===h)return r;var y=p[h];try{for(var _=n(i.default.dependencies),g=_.next();!g.done;g=_.next()){var b=g.value;if(y[b])try{for(var m=(s=void 0,n((0,l.flatten)(y[b]))),M=m.next();!M.done;M=m.next()){var k=M.value,w=(0,f.default)(k);r.has(w)||(r.add(w),o&&(0,t.funcExtractDeps)(w,r))}}catch(e){s={error:e}}finally{try{M&&!M.done&&(v=m.return)&&v.call(m)}finally{if(s)throw s.error}}}}catch(e){c={error:e}}finally{try{g&&!g.done&&(d=_.return)&&d.call(_)}finally{if(c)throw c.error}}return r}},7285:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return"function"==typeof e&&e.__forward_ref__?e():e}},1102:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return"undefined"==typeof window?r.g:window}},5590:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.getMockedNgDefOf=function(e,t){var r,n=null!==(r=e.mockOf)&&void 0!==r?r:e,u=(0,o.default)(a.NG_MOCKS),c=f(e,n,u);if(c&&!t)return c;if(c&&t&&(0,l.isMockedNgDefOf)(c,n,t))return c;throw new Error("There is no mock for ".concat((0,i.default)(n)))};var o=n(r(4201)),a=r(1763),i=n(r(2970)),l=r(732),u=n(r(8073)),f=function(e,t,r){if(r&&!r.has(t))throw new Error("There is no mock for ".concat((0,i.default)(t)));var n=r?r.get(t):void 0;return n===t&&(n=void 0),n||t===e?!n&&u.default.cacheDeclarations.has(t)&&(n=u.default.cacheDeclarations.get(t)):n=e,n}},2970:function(e,t){Object.defineProperty(t,"__esModule",{value:!0});var r=new RegExp("[^0-9a-z]+","mgi");t.default=function(e){var t;return"function"==typeof e&&e.name?t=e.name:"function"==typeof e?t="arrowFunction":"object"==typeof e&&e&&"InjectionToken"===e.ngMetadataName?t=e._desc:"object"==typeof e&&e&&"function"==typeof e.constructor&&(t=e.constructor.name),t||(t="unknown"),t.replace(r,"_")}},9335:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.getNgType=void 0;var o=n(r(4673)),a=r(4152);t.getNgType=function(e){if("string"!=typeof e){if((0,a.isNgInjectionToken)(e))return"Injectable";for(var t=(0,o.default)(e).decorators,r=t.length-1;r>=0;r-=1)if("Injectable"!==t[r])return t[r];return t.length>0?"Injectable":void 0}}},6739:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.getSourceOfMock=function(e){return"function"==typeof e&&e.mockOf?e.mockOf:e}},6297:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(3659);t.default=function(e){return e&&"object"==typeof e&&e.provide?e.provide:(0,n.isNgModuleDefWithProviders)(e)?e.ngModule:e&&"object"==typeof e&&e.directive?e.directive:e}},6804:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(2970)),a=r(5974);t.default=function(e,t){if(null==e)throw new Error("null / undefined has been passed into ".concat(t,". Please check that its import is correct."));if(!("MockPipe"===t&&(0,a.isNgDef)(e,"p")||"MockDirective"===t&&(0,a.isNgDef)(e,"d")||"MockComponent"===t&&(0,a.isNgDef)(e,"c")||"MockModule"===t&&(0,a.isNgDef)(e,"m"))){var r=function(e){return(0,a.isNgDef)(e,"p")?"pipe":(0,a.isNgDef)(e,"d")?"directive":(0,a.isNgDef)(e,"c")?"component":(0,a.isNgDef)(e,"m")?"module":(0,a.isNgDef)(e,"i")?"service":(0,a.isNgDef)(e,"t")?"token":""}(e);if(r&&"MockPipe"===t)throw new Error("".concat(t," accepts pipes, whereas ").concat((0,o.default)(e)," is a ").concat(r,"."));if(r&&"MockDirective"===t)throw new Error("".concat(t," accepts directives, whereas ").concat((0,o.default)(e)," is a ").concat(r,"."));if(r&&"MockComponent"===t)throw new Error("".concat(t," accepts components, whereas ").concat((0,o.default)(e)," is a ").concat(r,"."));if(r&&"MockModule"===t)throw new Error("".concat(t," accepts modules, whereas ").concat((0,o.default)(e)," is a ").concat(r,"."))}}},9628:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return!(!e||"function"!=typeof e&&"object"!=typeof e||!(e._isMockFunction&&e.mockName&&e.__annotations__))}},7105:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isMockControlValueAccessor=void 0;var o=n(r(2137));t.isMockControlValueAccessor=function(e){return!!(0,o.default)(e)&&!!e.__ngMocksConfig.isControlValueAccessor}},6763:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.isMockNgDef=function(e,t){return!!e.mockOf&&(!t||(0,n.isNgDef)(e.mockOf,t))};var n=r(5974)},7675:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isMockOf=function(e,t,r){return(0,o.default)(e)&&e.constructor===t&&(r?(0,a.isNgDef)(e.constructor,r):(0,a.isNgDef)(e.constructor))};var o=n(r(2137)),a=r(5974)},2650:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isMockValidator=void 0;var o=n(r(2137));t.isMockValidator=function(e){return!!(0,o.default)(e)&&!!e.__ngMocksConfig.isValidator}},2137:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return e&&"object"==typeof e&&!!e.__ngMocks}},732:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.isMockedNgDefOf=function(e,t,r){return"function"==typeof e&&e.mockOf===t&&(!r||(0,n.isNgDef)(e,r))};var n=r(5974)},5974:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.isNgDef=function(e,t){if("t"===t)return(0,n.isNgInjectionToken)(e);if("function"!=typeof e)return!1;var r=a(e,t),o=i(e,t),c=l(e,t),d=u(e,t),s=f(e,t);return r||o||c||d||s};var n=r(4152),o=r(7297),a=function(e,t){return(!t||"m"===t)&&(0,o.isNgType)(e,"NgModule")},i=function(e,t){return(!t||"c"===t)&&(0,o.isNgType)(e,"Component")},l=function(e,t){return(!t||"d"===t)&&(0,o.isNgType)(e,"Directive")},u=function(e,t){return(!t||"p"===t)&&(0,o.isNgType)(e,"Pipe")},f=function(e,t){return(!t||"i"===t)&&(0,o.isNgType)(e,"Injectable")}},4152:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.isNgInjectionToken=void 0,t.isNgInjectionToken=function(e){return e&&"object"==typeof e&&"InjectionToken"===e.ngMetadataName}},3659:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.isNgModuleDefWithProviders=void 0,t.isNgModuleDefWithProviders=function(e){return e&&"object"==typeof e&&"function"==typeof e.ngModule}},7297:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isNgType=void 0;var o=n(r(4673));t.isNgType=function(e,t){var r=(0,o.default)(e).decorators;if(0===r.length)return!1;var n=1;if("Injectable"===t&&-1!==r.indexOf("Injectable"))return!0;for(;"Injectable"===r[r.length-n];)n+=1;return r[r.length-n]===t}},6580:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isStandalone=function(e){var t=(0,a.getNgType)(e);return!(!t||"Injectable"===t)&&!0===(0,o.default)(e)[t].standalone};var o=n(r(4673)),a=r(9335)},6259:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(4673)),i=o(r(3295)),l=r(6456),u=o(r(6297)),f=function(e,t,r){var o,c,d,s,v,p;void 0===r&&(r=new Set);var h=(0,a.default)(e);try{for(var y=n(h.decorators),_=y.next();!_.done;_=y.next()){var g=_.value;try{for(var b=(d=void 0,n(i.default.dependencies)),m=b.next();!m.done;m=b.next()){var M=m.value;if(h[g][M])try{for(var k=(v=void 0,n((0,l.flatten)(h[g][M]))),w=k.next();!w.done;w=k.next()){var O=w.value,x=(0,u.default)(O);x&&!r.has(x)&&(r.add(x),t(x),f(x,t,r))}}catch(e){v={error:e}}finally{try{w&&!w.done&&(p=k.return)&&p.call(k)}finally{if(v)throw v.error}}}}catch(e){d={error:e}}finally{try{m&&!m.done&&(s=b.return)&&s.call(b)}finally{if(d)throw d.error}}}}catch(e){o={error:e}}finally{try{_&&!_.done&&(c=y.return)&&c.call(y)}finally{if(o)throw o.error}}};t.default=f},1433:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.MockAsyncValidatorProxy=t.MockValidatorProxy=t.MockControlValueAccessorProxy=void 0;var r=function(e,t,r,n){if(e.instance&&n&&(e.instance[n]=r),e.instance&&e.instance[t])return e.instance[t](r)},n=function(){function e(e){this.target=e}return e.prototype.registerOnChange=function(e){r(this,"registerOnChange",e,"__simulateChange")},e.prototype.registerOnTouched=function(e){r(this,"registerOnTouched",e,"__simulateTouch")},e.prototype.setDisabledState=function(e){r(this,"setDisabledState",e)},e.prototype.writeValue=function(e){r(this,"writeValue",e)},e}();t.MockControlValueAccessorProxy=n;var o=function(){function e(e){this.target=e}return e.prototype.registerOnValidatorChange=function(e){r(this,"registerOnValidatorChange",e,"__simulateValidatorChange")},e.prototype.validate=function(e){return this.instance&&this.instance.validate?this.instance.validate(e):null},e}();t.MockValidatorProxy=o;var a=function(){function e(e){this.target=e}return e.prototype.registerOnValidatorChange=function(e){r(this,"registerOnValidatorChange",e,"__simulateValidatorChange")},e.prototype.validate=function(e){if(this.instance&&this.instance.validate){var t=this.instance.validate(e);return void 0===t?Promise.resolve(null):t}return Promise.resolve(null)},e}();t.MockAsyncValidatorProxy=a},5980:function(e,t,r){var n,o=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0}),t.LegacyControlValueAccessor=void 0;var a=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return o(t,e),t.prototype.__simulateChange=function(){},t.prototype.__simulateTouch=function(){},t.prototype.__simulateValidatorChange=function(){},t}(r(1946).Mock);t.LegacyControlValueAccessor=a},1946:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o0&&((o=(0,c.default)(e._providers||(null===(n=e._compiler)||void 0===n?void 0:n.providers)).touches)||(o=new Set,t.providers=t.providers||[],t.providers.push({provide:O.NG_MOCKS_TOUCHES,useValue:o})),T(t,o)),o}(Y,K,J.touches);return Z&&v.ngMocks.flushTestBed(),ee&&function(e){var t,r;try{for(var i=o((0,b.mapEntries)(e)),l=i.next();!l.done;l=i.next()){var u=a(l.value,2),c=u[0],d=a(u[1],2),s=d[0],v=d[1];f.TestBed.ngMocksOverrides.set(c,n(n({},v),{override:s})),N(c,s)}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}}(ee),!te||Y._instantiated||Y._testModuleRef||function(e,t){var r,n;if(f.TestBed.ngMocksOverrides){var i=C.default.touches;C.default.touches=t;try{for(var l=o((0,b.flatten)(e.ngModule||[])),u=l.next();!u.done;u=l.next()){var c=u.value;E(c)}}catch(e){r={error:e}}finally{try{u&&!u.done&&(n=l.return)&&n.call(l)}finally{if(r)throw r.error}}!function(e){var t,r;try{for(var n=o((0,b.mapEntries)(C.default.getDefaults())),i=n.next();!i.done;i=n.next()){var l=a(i.value,2),u=l[0];"mock"===a(l[1],1)[0]&&((0,S.isNgDef)(u,"i")||(0,S.isNgDef)(u,"t"))&&(e.has(u)||f.TestBed.ngMocksOverrides.has(u)||A(u,e))}}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}}(t),C.default.touches=i}}(Y,te),e.call(t,K)}},I=function(e,t){return function(){return C.default.global.delete("builder:config"),C.default.global.delete("builder:module"),f.TestBed.ngMocksSelectors=void 0,function(e){var t,r,n;if(null===(n=e.ngMocksOverrides)||void 0===n?void 0:n.size){v.ngMocks.flushTestBed();try{for(var i=o((0,b.mapEntries)(e.ngMocksOverrides)),l=i.next();!l.done;l=i.next()){var u=a(l.value,2),f=u[0],c=u[1];N(f,c)}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}}e.ngMocksOverrides=void 0}(f.TestBed),e.call(t)}},V=function(e){if(!u.ViewContainerRef.ngMocksOverridesPatched&&((0,g.default)(u.ViewContainerRef,"ngMocksOverridesPatched",!0),e.createComponent)){var t=e.createComponent,r=(0,y.default)(t,void 0,void 0,(function(e){for(var r,n=[],o=1;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},l=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var u=l(r(8073)),f={id:{},level:"root"},c=u.default.global.get("reporter-stack")||[a({},f)];u.default.global.set("reporter-stack",c);var d=null!==(n=u.default.global.get("reporter-stack-push"))&&void 0!==n?n:[];u.default.global.set("reporter-stack-push",d);var s=null!==(o=u.default.global.get("reporter-stack-pop"))&&void 0!==o?o:[];u.default.global.set("reporter-stack-pop",s),t.default={current:function(){return c[c.length-1]},stackPop:function(){var e,t,r=c.pop();if(0===c.length&&c.push("root"===(null==r?void 0:r.level)?r:a({},f)),r&&"root"!==r.level)try{for(var n=i(s),o=n.next();!o.done;o=n.next())(0,o.value)(r,c)}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=n.return)&&t.call(n)}finally{if(e)throw e.error}}u.default.global.set("reporter-stack-id",c[c.length-1].id)},stackPush:function(){var e,t,r={};u.default.global.set("reporter-stack-id",r);var n={id:r,level:"runtime"};c.push(n);try{for(var o=i(d),a=o.next();!a.done;a=o.next())(0,a.value)(n,c)}catch(t){e={error:t}}finally{try{a&&!a.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}},subscribePop:function(e){-1===s.indexOf(e)&&s.push(e)},subscribePush:function(e){d.indexOf(e)&&d.push(e),c.length>0&&e(c[c.length-1],c)},unsubscribePop:function(e){var t=s.indexOf(e);-1!==t&&s.splice(t,1)},unsubscribePush:function(e){var t=d.indexOf(e);-1!==t&&d.splice(t,1)}}},8073:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3295)),i=o(r(1102)),l=o(r(2970)),u=function(e){return function(){return f.global.has(e)||f.global.set(e,new Map),f.global.get(e)}};(0,i.default)().ngMocksUniverse=(0,i.default)().ngMocksUniverse||{};var f=(0,i.default)().ngMocksUniverse;f.builtDeclarations=new Map,f.builtProviders=new Map,f.cacheDeclarations=new Map,f.cacheProviders=new Map,f.config=new Map,f.configInstance=new Map,f.flags=new Set(a.default.flags),f.global=new Map,f.touches=new Set,f.global.set("flags",{onMockBuilderMissingDependency:a.default.onMockBuilderMissingDependency,onMockInstanceRestoreNeed:a.default.onMockInstanceRestoreNeed,onTestBedFlushNeed:a.default.onTestBedFlushNeed}),f.getOverrides=u("overrides"),f.getDefaults=u("defaults"),f.getConfigMock=u("configMock");var c=function(e){var t;return(t=f.getDefaults().get(e))||(t="function"==typeof e?f.getDefaults().get("@".concat((0,l.default)(e))):void 0)?t:[]};f.getResolution=function(e){var t=f.config.get("ngMocksDepsResolution");return(null==t?void 0:t.has(e))?t.get(e):n(c(e),1)[0]},f.getBuildDeclaration=function(e){if(f.builtDeclarations.has(e))return f.builtDeclarations.get(e);var t=n(c(e),2),r=t[0],o=t[1];return"exclude"===r?null:r&&"keep"!==r?"replace"===r?o:void 0:e},f.hasBuildDeclaration=function(e){if(f.builtDeclarations.has(e))return void 0!==f.builtDeclarations.get(e);var t=n(c(e),1)[0];return!!t&&"mock"!==t};var d=function(e){return f.hasBuildDeclaration(e)},s=function(e){return f.getBuildDeclaration(e)};f.isExcludedDef=function(e){var t=f.getResolution(e);return(!t||"exclude"===t)&&d(e)&&null===s(e)},f.isProvidedDef=function(e){return d(e)&&null!==s(e)},f.getDefaults().set("@StoreDevtoolsModule",["exclude"]),f.indexValue=0,f.index=function(){return f.indexValue++},t.default=f},4117:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0});var o=r(6456),a=r(1763);t.default=function(e){var t,r,i,l,u;try{for(var f=n((0,o.flatten)(e||[])),c=f.next();!c.done;c=f.next()){var d=c.value;"object"==typeof d&&(d.provide===a.NG_MOCKS&&(i=d.useValue),d.provide===a.NG_MOCKS_OVERRIDES&&(l=d.useValue),d.provide===a.NG_MOCKS_TOUCHES&&(u=d.useValue))}}catch(e){t={error:e}}finally{try{c&&!c.done&&(r=f.return)&&r.call(f)}finally{if(t)throw t.error}}return{mocks:i,overrides:l,touches:u}}},9539:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockBuilderStash=void 0;var a=o(r(3295)),i=o(r(8073)),l=function(){function e(){this.data={}}return e.prototype.backup=function(){this.data={builtDeclarations:i.default.builtDeclarations,builtProviders:i.default.builtProviders,cacheDeclarations:i.default.cacheDeclarations,cacheProviders:i.default.cacheProviders,config:i.default.config,configInstance:i.default.configInstance,flags:i.default.flags,touches:i.default.touches},i.default.builtDeclarations=new Map,i.default.builtProviders=new Map,i.default.cacheDeclarations=new Map,i.default.cacheProviders=new Map,i.default.config=new Map,i.default.configInstance=new Map,i.default.flags=new Set(a.default.flags),i.default.touches=new Set},e.prototype.restore=function(){var e,t;try{for(var r=n(Object.keys(this.data)),o=r.next();!o.done;o=r.next()){var a=o.value;i.default[a]=this.data[a]}}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}},e}();t.MockBuilderStash=l},9755:function(e,t,r){var n,o=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),a=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(o,a){function i(e){try{u(n.next(e))}catch(e){a(e)}}function l(e){try{u(n.throw(e))}catch(e){a(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(i,l)}u((n=n.apply(e,t||[])).next())}))},i=this&&this.__generator||function(e,t){var r,n,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:l(0),throw:l(1),return:l(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function l(l){return function(u){return function(l){if(r)throw new TypeError("Generator is already executing.");for(;a&&(a=0,l[0]&&(i=0)),i;)try{if(r=1,n&&(o=2&l[0]?n.return:l[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,l[1])).done)return o;switch(n=0,o&&(l=[2&l[0],o.value]),l[0]){case 0:case 1:o=l;break;case 4:return i.label++,{value:l[1],done:!1};case 5:i.label++,n=l[1],l=[0];continue;case 7:l=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==l[0]&&2!==l[0])){i=0;continue}if(3===l[0]&&(!o||l[1]>o[0]&&l[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},u=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockBuilderPerformance=void 0;var f=r(2603),c=r(6456),d=u(r(8073)),s=r(9620),v=u(r(2739)),p=u(r(9049)),h=u(r(7570)),y=u(r(1271)),_=u(r(5633)),g=u(r(4503)),b=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return o(t,e),t.prototype.build=function(){var t=d.default.global;if(t.has("builder:module")&&t.has("builder:config")&&this.equalsTo(t.get("builder:config")))return(0,g.default)(t.get("builder:module"));t.has("builder:module")&&t.delete(t.get("builder:module"));var r=this.cloneConfig(),n=e.prototype.build.call(this);return t.set("builder:config",r),t.set("builder:module",n),(0,g.default)(n)},t.prototype.then=function(t,r){return a(this,void 0,Promise,(function(){var n,o;return i(this,(function(a){return(n=d.default.global).has("bullet")&&n.has("builder:module")&&n.has("builder:config")&&this.equalsTo(n.get("builder:config"))?[2,n.get(n.get("builder:module")).then(t,r)]:(n.has("bullet")&&n.has("bullet:reset")&&(console.warn("ngMocks.faster has zero effect due to changes in testing module between runs"),n.delete("bullet"),f.TestBed.resetTestingModule(),n.set("bullet",!0)),o=e.prototype.then.call(this,t,r),n.set(n.get("builder:module"),o),[2,o])}))}))},t.prototype.cloneConfig=function(){var e=(0,_.default)();return(0,c.mapValues)(this.beforeCC,e.beforeCC),(0,c.mapValues)(this.excludeDef,e.excludeDef),(0,c.mapValues)(this.keepDef,e.keepDef),(0,c.mapValues)(this.mockDef,e.mockDef),(0,c.mapValues)(this.replaceDef,e.replaceDef),(0,c.mapEntries)(this.configDef,e.configDef),(0,c.mapEntries)(this.defProviders,e.defProviders),(0,c.mapEntries)(this.defValue,e.defValue),(0,c.mapEntries)(this.providerDef,e.providerDef),e},t.prototype.equalsTo=function(e){var t,r,n,o,a,i;try{for(var u=l(["beforeCC","keepDef","replaceDef","excludeDef","mockDef"]),f=u.next();!f.done;f=u.next()){var c=f.value;if(!(0,y.default)(this[c],e[c]))return!1}}catch(e){t={error:e}}finally{try{f&&!f.done&&(r=u.return)&&r.call(u)}finally{if(t)throw t.error}}try{for(var d=l(["defValue"]),s=d.next();!s.done;s=d.next())if(c=s.value,!(0,p.default)(this[c],e[c]))return!1}catch(e){n={error:e}}finally{try{s&&!s.done&&(o=d.return)&&o.call(d)}finally{if(n)throw n.error}}try{for(var _=l(["providerDef","defProviders"]),g=_.next();!g.done;g=_.next())if(c=g.value,!(0,p.default)(this[c],e[c],h.default))return!1}catch(e){a={error:e}}finally{try{g&&!g.done&&(i=_.return)&&i.call(_)}finally{if(a)throw a.error}}return(0,p.default)(this.configDef,e.configDef,v.default)},t}(s.MockBuilderPromise);t.MockBuilderPerformance=b},9620:function(e,t,r){var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(o,a){function i(e){try{u(n.next(e))}catch(e){a(e)}}function l(e){try{u(n.throw(e))}catch(e){a(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(i,l)}u((n=n.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var r,n,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:l(0),throw:l(1),return:l(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function l(l){return function(u){return function(l){if(r)throw new TypeError("Generator is already executing.");for(;a&&(a=0,l[0]&&(i=0)),i;)try{if(r=1,n&&(o=2&l[0]?n.return:l[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,l[1])).done)return o;switch(n=0,o&&(l=[2&l[0],o.value]),l[0]){case 0:case 1:o=l;break;case 4:return i.label++,{value:l[1],done:!1};case 5:i.label++,n=l[1],l=[0];continue;case 7:l=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==l[0]&&2!==l[0])){i=0;continue}if(3===l[0]&&(!o||l[1]>o[0]&&l[1]0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},u=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockBuilderPromise=void 0;var f=r(2603),c=u(r(5551)),d=r(6456),s=u(r(2970)),v=r(5974),p=r(3659),h=u(r(8073)),y=r(3771),_=r(9539),g=u(r(7140)),b=u(r(1649)),m=u(r(4387)),M=u(r(1267)),k=u(r(9961)),w=u(r(7703)),O=u(r(5195)),x=u(r(1391)),j=u(r(7235)),D=u(r(8792)),S=u(r(9102)),P=function(e){return(0,p.isNgModuleDefWithProviders)(e)?{def:e.ngModule,providers:e.providers}:{def:e,providers:void 0}},C=function(e,t,r){return r?i(i([],a(Array.isArray(t)?t:[]),!1),[e],!1):e},N={},T=function(){function e(e){this.configDefault=e,this.beforeCC=new Set,this.configDef=new Map,this.defProviders=new Map,this.defValue=new Map,this.excludeDef=new Set,this.keepDef=new Set,this.mockDef=new Set,this.providerDef=new Map,this.replaceDef=new Set,this.stash=new _.MockBuilderStash,"undefined"!=typeof Symbol&&(this[Symbol.toStringTag]="Promise")}return e.prototype.beforeCompileComponents=function(e){return this.beforeCC.add(e),this},e.prototype.build=function(){this.stash.backup();var e=new c.default;h.default.config.set("mockNgDefResolver",e),h.default.flags.add("hasRootModule");try{var t=this.combineParams(),r=(0,x.default)(t,(0,j.default)(t));return(0,g.default)(r,t,e),(0,O.default)(r,t,e),(0,w.default)(r),(0,b.default)(),r.providers.push((0,M.default)(),(0,k.default)(),(0,m.default)(this.replaceDef,this.defValue),y.MockBuilder),r}finally{h.default.flags.delete("hasRootModule"),h.default.config.delete("mockNgDefResolver"),this.stash.restore()}},e.prototype.catch=function(e){return n(this,void 0,Promise,(function(){return o(this,(function(t){return[2,this.then().catch(e)]}))}))},e.prototype.exclude=function(e){return this.wipe(e),this.excludeDef.add(e),this.setConfigDef(e),this},e.prototype.finally=function(e){return n(this,void 0,Promise,(function(){return o(this,(function(t){return[2,this.then().finally(e)]}))}))},e.prototype.keep=function(e,t){var r=P(e),n=r.def,o=r.providers,l=this.keepDef.has(n)?this.defProviders.get(n):[];return this.wipe(n),this.keepDef.add(n),o&&this.defProviders.set(n,i(i([],a(l||[]),!1),a(o),!1)),this.setConfigDef(n,t),this},e.prototype.mock=function(e,t,r){void 0===t&&(t=N);var n=P(e),o=n.def,l=n.providers,u=(0,D.default)(o,t,r,N),f=u.config,c=u.mock;if((0,v.isNgDef)(c)&&(0,v.isNgDef)(e)&&!(0,v.isNgDef)(e,"t"))throw new Error(["MockBuilder.mock(".concat((0,s.default)(e),") received a class when its shape is expected."),"Please try ngMocks.defaultMock instead."].join(" "));var d=this.mockDef.has(o)?this.defProviders.get(o):[];return this.wipe(o),this.mockDef.add(o),l&&this.defProviders.set(o,i(i([],a(d||[]),!1),a(l),!1)),this.setDefValue(o,c),this.setConfigDef(o,f),this},e.prototype.provide=function(e){var t,r;try{for(var n=l((0,d.flatten)(e)),o=n.next();!o.done;o=n.next()){var a=o.value,i=(0,S.default)(a),u=i.provide,f=i.multi,c=this.providerDef.has(u)?this.providerDef.get(u):[];this.providerDef.set(u,C(a,c,f))}}catch(e){t={error:e}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}return this},e.prototype.replace=function(e,t,r){if(!(0,v.isNgDef)(t)||!(0,v.isNgDef)(e)||(0,v.isNgDef)(t,"i")||(0,v.isNgDef)(e,"i"))throw new Error("Cannot replace the declaration, both have to be a Module, a Component, a Directive or a Pipe, for Providers use `.mock` or `.provide`");return this.wipe(e),this.replaceDef.add(e),this.defValue.set(e,t),this.setConfigDef(e,r),this},e.prototype.then=function(e,t){return n(this,void 0,Promise,(function(){var r,n=this;return o(this,(function(o){return r=new Promise((function(e){var t,r,o=f.TestBed.configureTestingModule(n.build());try{for(var a=l((0,d.mapValues)(n.beforeCC)),i=a.next();!i.done;i=a.next())(0,i.value)(o)}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}o.compileComponents().then((function(){e({testBed:o})}))})),[2,r.then(e,t)]}))}))},e.prototype.combineParams=function(){return{configDef:this.configDef,configDefault:this.configDefault,defProviders:this.defProviders,defValue:this.defValue,excludeDef:this.excludeDef,keepDef:this.keepDef,mockDef:this.mockDef,providerDef:this.providerDef,replaceDef:this.replaceDef}},e.prototype.setConfigDef=function(e,t){!t&&this.configDef.has(e)||this.configDef.set(e,null!=t?t:this.configDefault)},e.prototype.setDefValue=function(e,t){t===N?this.defValue.delete(e):this.defValue.set(e,t)},e.prototype.wipe=function(e){this.defProviders.delete(e),this.defValue.delete(e),this.excludeDef.delete(e),this.keepDef.delete(e),this.mockDef.delete(e),this.providerDef.delete(e),this.replaceDef.delete(e)},e}();t.MockBuilderPromise=T},3771:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockBuilder=s;var i=a(r(3174)),l=r(6456),u=r(6580),f=a(r(8073)),c=a(r(5020)),d=r(9755);function s(){for(var e,t,r,a,s,v,p=[],h=0;h=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0});var o=r(6456);t.default=function(e,t,r){var a,i;if(void 0===r&&(r=function(e,t){return e===t}),!t||t.size!==e.size)return!1;try{for(var l=n((0,o.mapKeys)(e)),u=l.next();!u.done;u=l.next()){var f=u.value;if(!t.has(f))return!1;if(!r(t.get(f),e.get(f)))return!1}}catch(e){a={error:e}}finally{try{u&&!u.done&&(i=l.return)&&i.call(l)}finally{if(a)throw a.error}}return!0}},7570:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=function(e,t){for(var r,o,a=[],i=2;i=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0});var o=r(6456);t.default=function(e,t){var r,a;if(!t||t.size!==e.size)return!1;try{for(var i=n((0,o.mapValues)(e)),l=i.next();!l.done;l=i.next()){var u=l.value;if(!t.has(u))return!1}}catch(e){r={error:e}}finally{try{l&&!l.done&&(a=i.return)&&a.call(i)}finally{if(r)throw r.error}}return!0}},2801:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(5432));t.default=function(e,t){return!(!(0,a.default)(t,e)||"object"==typeof t&&"object"==typeof e&&function(e,t){var r,o;if(Object.keys(e).length!==Object.keys(t).length)return!0;try{for(var i=n(Object.keys(e)),l=i.next();!l.done;l=i.next()){var u=l.value;if(!(0,a.default)(e[u],t[u]))return!0}}catch(e){r={error:e}}finally{try{l&&!l.done&&(o=i.return)&&o.call(i)}finally{if(r)throw r.error}}return!1}(t,e))}},5432:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(9382));t.default=function(e,t){return e===t||("boolean"!=typeof e&&"boolean"!=typeof t||e===t)&&e.$implicit===t.$implicit&&(0,o.default)(e.variables,t.variables)}},9382:function(e,t){var r=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){return e===t||!(e&&!t||!e&&t)&&!function(e,t){var n,o,a=Object.keys(e),i=Object.keys(t);if(a.length!==i.length)return!0;try{for(var l=r(a),u=l.next();!u.done;u=l.next()){var f=u.value;if(e[f]!==t[f])return!0}}catch(e){n={error:e}}finally{try{u&&!u.done&&(o=l.return)&&o.call(l)}finally{if(n)throw n.error}}return!1}(e,t)}},5633:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{beforeCC:new Set,configDef:new Map,defProviders:new Map,defValue:new Map,excludeDef:new Set,keepDef:new Set,mockDef:new Set,providerDef:new Map,replaceDef:new Set}}},4503:function(e,t){var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8911)),l=o(r(6297)),u=o(r(8073)),f=o(r(3663));t.default=function(e,t,r){var o,c,d,s,v,p,h=t.providerDef,y=t.mockDef;try{for(var _=n((0,a.mapValues)(h)),g=_.next();!g.done;g=_.next()){var b=g.value;e.providers.push(b)}}catch(e){o={error:e}}finally{try{g&&!g.done&&(c=_.return)&&c.call(_)}finally{if(o)throw o.error}}try{for(var m=n((0,a.flatten)(e.providers)),M=m.next();!M.done;M=m.next()){b=M.value;var k=(0,l.default)(b);u.default.touches.add(k),k!==b&&b.deps&&(0,a.extractDependency)(b.deps,u.default.config.get("ngMocksDeps"))}}catch(e){d={error:e}}finally{try{M&&!M.done&&(s=m.return)&&s.call(m)}finally{if(d)throw d.error}}try{for(var w=n((0,a.mapValues)(y)),O=w.next();!O.done;O=w.next()){var x=O.value;u.default.touches.has(x)||"root"!==(0,i.default)(x)||(e.providers.push((0,f.default)(x,r)),u.default.touches.add(x))}}catch(e){v={error:e}}finally{try{O&&!O.done&&(p=w.return)&&p.call(w)}finally{if(v)throw v.error}}}},1649:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(2603),i=r(6456),l=o(r(6297)),u=o(r(8073));t.default=function(){var e,t,r=(0,a.getTestBed)();if(r.ngModule)try{for(var o=n((0,i.flatten)(r.ngModule)),f=o.next();!f.done;f=o.next()){var c=f.value;u.default.touches.add((0,l.default)(c))}}catch(t){e={error:t}}finally{try{f&&!f.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}}},646:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t,r){"function"==typeof e&&-1===r.indexOf(e)&&(r.push(e),t.push(e))}},4387:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(1381)),l=r(1763),u=o(r(8073)),f=o(r(5884)),c=o(r(4483));t.default=function(e,t){var r,o,d,s,v=new Map;try{for(var p=n((0,a.mapValues)(u.default.touches)),h=p.next();!h.done;h=p.next()){var y=h.value,_=u.default.getBuildDeclaration(y)||y;if(!(0,c.default)(e,t,y,_)){var g=(0,i.default)(_),b=(0,f.default)(g);if(b){var m={};try{for(var M=(d=void 0,n(Object.keys(b))),k=M.next();!k.done;k=M.next()){var w=k.value;m[w]=g[w]}}catch(e){d={error:e}}finally{try{k&&!k.done&&(s=M.return)&&s.call(M)}finally{if(d)throw d.error}}v.set(_,[{set:b},{set:m}])}}}}catch(e){r={error:e}}finally{try{h&&!h.done&&(o=p.return)&&o.call(p)}finally{if(r)throw r.error}}return{provide:l.NG_MOCKS_OVERRIDES,useValue:v}}},1267:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=r(6456),u=r(1763),f=i(r(8073));t.default=function(){var e,t,r=new Map;try{for(var i=a(o(o(o(o([],n((0,l.mapEntries)(f.default.builtProviders)),!1),n((0,l.mapEntries)(f.default.builtDeclarations)),!1),n((0,l.mapEntries)(f.default.cacheDeclarations)),!1),n((0,l.mapEntries)(f.default.cacheProviders)),!1)),c=i.next();!c.done;c=i.next()){var d=n(c.value,2),s=d[0],v=d[1];r.has(s)||r.set(s,v)}}catch(t){e={error:t}}finally{try{c&&!c.done&&(t=i.return)&&t.call(i)}finally{if(e)throw e.error}}return{provide:u.NG_MOCKS,useValue:r}}},9961:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=r(1763),l=o(r(8073));t.default=function(){var e,t,r=new Set;try{for(var o=n((0,a.mapValues)(l.default.touches)),u=o.next();!u.done;u=o.next()){var f=u.value,c=l.default.getBuildDeclaration(f);void 0===c&&(c=f),r.add(f),r.add(c)}}catch(t){e={error:t}}finally{try{u&&!u.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}return{provide:i.NG_MOCKS_TOUCHES,useValue:r}}},8608:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(7285));t.default=function(e){var t,r;if(e){var o;try{for(var i=n(e),l=i.next();!l.done;l=i.next()){var u=l.value;u&&"object"==typeof u&&u.token&&(o=u.token),o||!u||"object"==typeof u&&u.ngMetadataName||(o=u)}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}return(0,a.default)(o)}}},5884:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(8073)),i=o(r(224));t.default=function(e){if(e){var t=a.default.flags.has("skipMock");t||a.default.flags.add("skipMock");var r=n((0,i.default)(e),2),o=r[0],l=r[1];if(t||a.default.flags.delete("skipMock"),o)return l}}},8703:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(8749)),i=r(1763),l=o(r(8073)),u=o(r(2504)),f=o(r(646)),c=o(r(8608)),d=o(r(1978)),s=o(r(7213)),v=o(r(4995));t.default=function(e){var t,r,o,p,h,y,_=new Set,g=(0,d.default)(),b=g.buckets,m=g.touched;try{for(var M=n(b),k=M.next();!k.done;k=M.next()){var w=k.value;try{for(var O=(o=void 0,n(w)),x=O.next();!x.done;x=O.next()){var j=x.value;(0,u.default)(_,e,j);try{for(var D=(h=void 0,n((0,a.default)(j))),S=D.next();!S.done;S=D.next()){var P=S.value,C=(0,c.default)(P);(0,s.default)(C),(0,v.default)(C)||((0,f.default)(C,m,w),e.has(i.NG_MOCKS_ROOT_PROVIDERS)||!l.default.config.get("ngMocksDepsSkip").has(j)?_.add(C):l.default.config.get("ngMocksDepsSkip").add(C))}}catch(e){h={error:e}}finally{try{S&&!S.done&&(y=D.return)&&y.call(D)}finally{if(h)throw h.error}}}}catch(e){o={error:e}}finally{try{x&&!x.done&&(p=O.return)&&p.call(O)}finally{if(o)throw o.error}}}}catch(e){t={error:e}}finally{try{k&&!k.done&&(r=M.return)&&r.call(M)}finally{if(t)throw t.error}}return _}},1978:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=r(860),u=i(r(3174)),f=r(6456),c=r(1763),d=r(5974),s=i(r(1365)),v=function(e,t){if(t){var r=t.resolveComponentFactory;t.resolveComponentFactory=(0,s.default)(r,void 0,void 0,(function(a){for(var i,l=[],u=1;u=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=r(1763),l=r(4152),u=o(r(8073)),f=o(r(3663)),c=o(r(9465)),d=o(r(8703));t.default=function(e,t,r){var o,s,v=t.keepDef,p=t.mockDef,h=v.has(i.NG_MOCKS_ROOT_PROVIDERS)?new Set:(0,d.default)(p);if(h.size>0){var y=function(t){var n=(0,f.default)(t,r);if(n)e.providers.push(n);else if((0,l.isNgInjectionToken)(t)){var o=u.default.config.has("ngMocksMulti")&&u.default.config.get("ngMocksMulti").has(t);e.providers.push((0,c.default)(t,(function(){return o?[]:void 0})))}};try{for(var _=n((0,a.mapValues)(h)),g=_.next();!g.done;g=_.next())y(g.value)}catch(e){o={error:e}}finally{try{g&&!g.done&&(s=_.return)&&s.call(_)}finally{if(o)throw o.error}}}}},2710:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8073));t.default=function(e){var t,r,o=i.default.builtDeclarations,l=i.default.builtProviders,u=i.default.config.get("ngMocksDepsResolution");try{for(var f=n((0,a.mapValues)(e)),c=f.next();!c.done;c=f.next()){var d=c.value;o.set(d,null),l.set(d,null),u.set(d,"exclude")}}catch(e){t={error:e}}finally{try{c&&!c.done&&(r=f.return)&&r.call(f)}finally{if(t)throw t.error}}}},7651:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=r(5204),l=o(r(8073));t.default=function(e,t){var r,o,u=new Set,f=l.default.builtDeclarations,c=l.default.builtProviders,d=l.default.config.get("ngMocksDepsResolution");try{for(var s=n((0,a.mapValues)(e)),v=s.next();!v.done;v=s.next()){var p=v.value;f.set(p,p),c.set(p,p),d.set(p,"keep"),t.get(p).shallow&&(0,i.funcExtractDeps)(p,u)}}catch(e){r={error:e}}finally{try{v&&!v.done&&(o=s.return)&&o.call(s)}finally{if(r)throw r.error}}return u}},102:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8073)),l=o(r(4161));t.default=function(e,t){var r,o,u=i.default.builtDeclarations,f=i.default.config.get("ngMocksDepsResolution");try{for(var c=n((0,a.mapValues)(e)),d=c.next();!d.done;d=c.next()){var s=d.value,v=!i.default.touches.has(s);f.set(s,"mock"),u.set(s,void 0),(0,l.default)(s,t),v&&i.default.touches.delete(s)}}catch(e){r={error:e}}finally{try{d&&!d.done&&(o=c.return)&&o.call(c)}finally{if(r)throw r.error}}}},3680:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8073));t.default=function(e,t){var r,n=null!==(r=o.default.config.get("mockNgDefResolver").get(e))&&void 0!==r?r:o.default.getBuildDeclaration(e),a=t.has(e)?t.get(e):void 0;return a?{ngModule:n,providers:a}:n}},1159:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=r(6456),u=r(5974),f=i(r(8073)),c=r(3821),d=i(r(224)),s=i(r(4673)),v=i(r(8150));t.default=function(e,t,r,i){var p,h,y,_,g,b,m,M,k,w=new Map;try{for(var O=a(o(o(o([],n((0,l.mapValues)(e)),!1),n((0,l.mapValues)(t)),!1),n((0,l.mapValues)(r)),!1)),x=O.next();!x.done;x=O.next()){var j=x.value,D=(0,s.default)(j),S=o(o(o([],n(null!==(g=i.get(j))&&void 0!==g?g:[]),!1),n(null!==(m=null===(b=D.Component)||void 0===b?void 0:b.providers)&&void 0!==m?m:[]),!1),n(null!==(k=null===(M=D.Directive)||void 0===M?void 0:M.providers)&&void 0!==k?k:[]),!1),P=!f.default.touches.has(j);t.has(j)||f.default.flags.add("skipMock");var C=(0,u.isNgDef)(j,"m");if(S.length>0){var N=n((0,d.default)({providers:S,skipMarkProviders:!C,skipExports:!0}),2)[1];w.set(j,N.providers)}C&&f.default.builtDeclarations.set(j,(0,c.MockModule)(j)),f.default.flags.delete("skipMock"),P&&f.default.touches.delete(j)}}catch(e){p={error:e}}finally{try{x&&!x.done&&(h=O.return)&&h.call(O)}finally{if(p)throw p.error}}try{for(var T=a((0,l.mapValues)(t)),E=T.next();!E.done;E=T.next())j=E.value,(0,v.default)(j)}catch(e){y={error:e}}finally{try{E&&!E.done&&(_=T.return)&&_.call(T)}finally{if(y)throw y.error}}return w}},1391:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8073));t.default=function(e,t){var r,o,l=i.default.builtDeclarations,u=i.default.config.get("ngMocksDepsResolution");try{for(var f=n((0,a.mapValues)(e)),c=f.next();!c.done;c=f.next()){var d=c.value;l.set(d,t.get(d)),u.set(d,"replace")}}catch(e){r={error:e}}finally{try{c&&!c.done&&(o=f.return)&&o.call(f)}finally{if(r)throw r.error}}}},7235:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=r(6456),u=r(5204),f=i(r(8073)),c=i(r(2710)),d=i(r(7651)),s=i(r(102)),v=i(r(1159)),p=i(r(8500));t.default=function(e){var t,r,i,h,y,_,g,b,m,M,k,w,O=e.configDef,x=e.defProviders,j=e.defValue,D=e.excludeDef,S=e.keepDef,P=e.mockDef,C=e.replaceDef;f.default.flags.add("cachePipe"),f.default.config.set("ngMocksMulti",new Set),f.default.config.set("ngMocksDeps",new Set),f.default.config.set("ngMocksDepsSkip",new Set),f.default.config.set("ngMocksDepsResolution",new Map);var N=(0,d.default)(S,O);try{for(var T=o((0,l.mapValues)(N)),E=T.next();!E.done;E=T.next()){var A=E.value;f.default.touches.add(A)}}catch(e){t={error:e}}finally{try{E&&!E.done&&(r=T.return)&&r.call(T)}finally{if(t)throw t.error}}try{for(var R=o((0,l.mapValues)(S)),I=R.next();!I.done;I=R.next())A=I.value,N.add(A),(0,u.funcExtractDeps)(A,N,!0)}catch(e){i={error:e}}finally{try{I&&!I.done&&(h=R.return)&&h.call(R)}finally{if(i)throw i.error}}try{for(var V=o((0,l.mapValues)(P)),B=V.next();!B.done;B=V.next())A=B.value,N.add(A),(0,u.funcExtractDeps)(A,N,!0)}catch(e){y={error:e}}finally{try{B&&!B.done&&(_=V.return)&&_.call(V)}finally{if(y)throw y.error}}try{for(var F=o((0,l.mapValues)(C)),L=F.next();!L.done;L=F.next())A=L.value,N.add(A),(0,u.funcExtractDeps)(A,N,!0)}catch(e){g={error:e}}finally{try{L&&!L.done&&(b=F.return)&&b.call(F)}finally{if(g)throw g.error}}try{for(var K=o((0,l.mapValues)(N)),G=K.next();!G.done;G=K.next())if(A=G.value,!O.has(A)){var q=f.default.getResolution(A);"replace"===q?(C.add(A),j.set(A,f.default.getBuildDeclaration(A))):"keep"===q?S.add(A):"exclude"===q?D.add(A):("mock"===q||f.default.touches.has(A))&&P.add(A),O.set(A,f.default.touches.has(A)?{dependency:!0,__internal:!0}:{})}}catch(e){m={error:e}}finally{try{G&&!G.done&&(M=K.return)&&M.call(K)}finally{if(m)throw m.error}}try{for(var U=o((0,l.mapEntries)(O)),z=U.next();!z.done;z=U.next()){var W=a(z.value,2),H=W[0],$=W[1];f.default.config.set(H,n(n(n({},f.default.getConfigMock().get(H)),$),{defValue:j.get(H)}))}}catch(e){k={error:e}}finally{try{z&&!z.done&&(w=U.return)&&w.call(U)}finally{if(k)throw k.error}}return(0,p.default)(C,j),(0,c.default)(D),(0,s.default)(P,j),(0,v.default)(S,P,C,x)}},8792:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(5974);t.default=function(e,t,r,o){var a=e===t?o:t,i=null!=r?r:t!==o&&"object"==typeof t?t:void 0;return(0,n.isNgDef)(e,"p")&&"function"==typeof t&&t!==e&&!(0,n.isNgDef)(t,"p")?(a={transform:t},i=r):!(0,n.isNgDef)(e,"i")&&(0,n.isNgDef)(e)||(i=r),{config:i,mock:a=a===i?o:a}}},9102:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(6297));t.default=function(e){var t=(0,o.default)(e);return{multi:t!==e&&e.multi,provide:t}}},70:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(4358),a=n(r(3295)),i=n(r(8911)),l=r(4152),u=n(r(8073));t.default=function(e){if(function(e){if(!e||e===o.DOCUMENT||u.default.touches.has(e))return!0;var t=function(e){var t=u.default.getResolution(e);return"keep"===t||"exclude"===t||"mock"!==t&&void 0}(e);return void 0!==t?t:"function"==typeof e&&-1!==a.default.neverMockProvidedFunction.indexOf(e.name)||!(!(0,l.isNgInjectionToken)(e)||-1===a.default.neverMockToken.indexOf(e.toString()))}(e))return!0;var t=(0,i.default)(e);return!("function"!=typeof e||t&&"platform"!==t)}},4483:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8073));t.default=function(e,t,r,n){return!!o.default.cacheDeclarations.has(n)||!(!e.has(r)||n!==t.get(r))}},4995:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8073)),a=n(r(70));t.default=function(e){return!!(0,a.default)(e)||o.default.config.get("ngMocksDepsSkip").has(e)}},8150:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(5974),a=n(r(8073)),i=r(5395),l=r(8339),u=r(2937);t.default=function(e){void 0===a.default.builtDeclarations.get(e)&&((0,o.isNgDef)(e,"c")&&a.default.builtDeclarations.set(e,(0,i.MockComponent)(e)),(0,o.isNgDef)(e,"d")&&a.default.builtDeclarations.set(e,(0,l.MockDirective)(e)),(0,o.isNgDef)(e,"p")&&a.default.builtDeclarations.set(e,(0,u.MockPipe)(e)))}},4161:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(5974),a=n(r(8073)),i=n(r(8195)),l=n(r(9465)),u=n(r(2415));t.default=function(e,t){if((0,o.isNgDef)(e,"i")&&t.has(e)){var r=a.default.config.get(e),n=t.get(e);a.default.builtProviders.set(e,(0,l.default)(e,void 0,(function(e){return function(e,t,r){return r.precise?t:(0,i.default)(e,t)}(e,n,r)})))}else(0,o.isNgDef)(e,"i")&&a.default.builtProviders.set(e,(0,u.default)(e,!0));if(!(0,o.isNgDef)(e)&&t.has(e)){var f=t.get(e);a.default.builtProviders.set(e,(0,l.default)(e,void 0,(function(){return f})))}else(0,o.isNgDef)(e)||a.default.builtProviders.set(e,(0,u.default)(e,!0))}},2938:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},5395:function(e,t,r){var n,o=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),a=this&&this.__assign||function(){return a=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},l=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},u=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockComponents=function(){for(var e=[],t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0});var o=r(860),a={read:o.ViewContainerRef,static:!1},i={read:o.TemplateRef,static:!1},l=function(e,t){var r="
    ");return"").concat(r,"")},u=function(e){return!(e.isViewQuery||e.read&&e.read!==o.TemplateRef||"string"!=typeof e.selector&&!e.read)};t.default=function(e){var t,r,f=[""];if(!e)return f.join("");try{for(var c=n(Object.keys(e)),d=c.next();!d.done;d=c.next()){var s=d.value,v=e[s];if(0!==s.indexOf("__mock")&&u(v)){if("string"==typeof v.selector){var p=v.selector.replace(new RegExp("\\W","mg"),"_");e["__vcrIf_key_".concat(p)]=new o.ViewChild("ngIf_key_".concat(p),a),e["__trIf_key_".concat(p)]=new o.ViewChild("ngIf_key_".concat(p),i),e["__mockView_key_".concat(p)]=new o.ViewChild("key_".concat(p),a),e["__mockTpl_key_".concat(p)]=v,f.push(l(p,"key"))}e["__vcrIf_prop_".concat(s)]=new o.ViewChild("ngIf_prop_".concat(s),a),e["__trIf_prop_".concat(s)]=new o.ViewChild("ngIf_prop_".concat(s),i),e["__mockView_prop_".concat(s)]=new o.ViewChild("prop_".concat(s),a),f.push(l(s,"prop"))}}}catch(e){t={error:e}}finally{try{d&&!d.done&&(r=c.return)&&r.call(c)}finally{if(t)throw t.error}}return f.join("")}},8970:function(e,t){var r=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){if("string"==typeof e)return["key","__mockTpl_key_".concat(e),e,void 0];var t=r(e),n=t[0],o=t.slice(1);return["prop",n,n,o.length>0?o:void 0]}},2350:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},5269:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockDeclarations=function(){for(var e=[],t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(1184)),l=a(r(6755)),u=a(r(1692));t.default=function(e,t){return function(r){return!!function(e,t,r){for(var n,a,i,l=(null===(n=e.injector._tNode)||void 0===n?void 0:n.attrs)||[],u=2,f=0;f0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(5465)),i=o(r(908));t.default=function(e){return function(t){var r=n((0,i.default)(t),2),o=r[0];return-1!==r[1].indexOf(e)||!!(0,a.default)(o,e)}}},12:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4201)),a=r(6739);t.default=function(e){var t=(0,a.getSourceOfMock)(e);return function(e){return!!e&&-1!==e.providerTokens.indexOf(t)&&void 0!==(0,o.default)(t,e.injector)}}},1863:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return function(t){return!!t.references[e]}}},5494:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(908)),i=o(r(4747));t.default=function(e){return function(t){var r=n((0,a.default)(t),1)[0];return(0,i.default)(r,e)}}},5465:function(e,t){var r=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){var n,o,a,i;try{for(var l=r(e),u=l.next();!u.done;u=l.next()){var f=u.value.match(/\[([^=\]]+)/g);if(f)try{for(var c=(a=void 0,r(f)),d=c.next();!d.done;d=c.next())if(d.value==="[".concat(t))return!0}catch(e){a={error:e}}finally{try{d&&!d.done&&(i=c.return)&&i.call(c)}finally{if(a)throw a.error}}}}catch(e){n={error:e}}finally{try{u&&!u.done&&(o=l.return)&&o.call(l)}finally{if(n)throw n.error}}return!1}},4476:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8864)),a=n(r(8060)),i=n(r(12)),l=n(r(1863)),u=n(r(5494));t.default=function(e){if(function(e){return Array.isArray(e)&&1===e.length&&"string"==typeof e[0]}(e))return(0,o.default)(e[0]);if(function(e){return Array.isArray(e)&&2===e.length&&"string"==typeof e[0]}(e))return(0,a.default)(e[0],e[1]);if(function(e){return"string"==typeof e&&0===e.indexOf("#")&&e.length>1}(e))return(0,l.default)(e.slice(1));if(function(e){return"string"==typeof e&&0!==e.indexOf("#")&&e.length>0}(e))return(0,u.default)(e);if(function(e){return"function"==typeof e}(e))return(0,i.default)(e);throw new Error("Unknown selector")}},908:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0});var n=new RegExp("\\[.*?\\]","g");t.default=function(e,t){var o,a,i,l;try{for(var u=r(e),f=u.next();!f.done;f=u.next()){var c=f.value.replace(n,"").split(",");try{for(var d=(i=void 0,r(c)),s=d.next();!s.done;s=d.next())if(s.value.trim()===t)return!0}catch(e){i={error:e}}finally{try{s&&!s.done&&(l=d.return)&&l.call(d)}finally{if(i)throw i.error}}}}catch(e){o={error:e}}finally{try{f&&!f.done&&(a=u.return)&&a.call(u)}finally{if(o)throw o.error}}return!1}},3942:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return"#text"===e.nativeNode.nodeName}},5079:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){return!(!e||!t)&&e===t}},6121:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(3942));t.default=function(e){return(0,o.default)(e)?void 0:e.injector._tNode||e.injector.elDef||void 0}},1061:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(860),l=a(r(4201)),u=a(r(6121)),f=function(e,t){var r=function(e,t){if(e!==t&&"#comment"===t.nativeNode.nodeName)return(0,l.default)(i.ViewContainerRef,t.injector)}(e,t);if(!r)return[];for(var n=[],o=0;o0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(590)),i=o(r(5687)),l=o(r(951)),u=o(r(4476)),f=o(r(3942)),c=o(r(6547)),d=o(r(8344));t.default=function(){for(var e=[],t=0;t0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(590)),i=o(r(5687)),l=o(r(951)),u=o(r(4811)),f=o(r(4476)),c=o(r(3942)),d=o(r(6547)),s=o(r(8344)),v={};t.default=function(){for(var e=[],t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3942)),i=o(r(5079)),l=o(r(6121)),u=o(r(1061));t.default=function(e){var t,r,o,f,c=(0,l.default)(e);if(!c||(0,a.default)(e))return[];var d=void 0!==e.childNodes,s=[];try{for(var v=n(e.childNodes||(null===(o=e.parent)||void 0===o?void 0:o.childNodes)||[]),p=v.next();!p.done;p=v.next()){var h=p.value,y=(0,u.default)(h);(d||(0,i.default)(c,y))&&(y&&!(0,i.default)(c,y)||s.push(h))}}catch(e){t={error:e}}finally{try{p&&!p.done&&(r=v.return)&&r.call(v)}finally{if(t)throw t.error}}if("BODY"===(null===(f=e.parent)||void 0===f?void 0:f.name)){for(var _=e.parent.childNodes,g=_.length,b=0,m=_.length-1;m>=0;m-=1)if("#comment"===(h=_[m]).nativeNode.nodeName)b=m;else if(h.nativeNode===e.nativeNode){g=m+1;break}for(m=g;m=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(5079)),i=o(r(6121)),l=o(r(1061));t.default=function(e,t){var r,o,u;if(t)return t;var f=(0,l.default)(e),c=e.parent?(0,i.default)(e.parent):void 0;if(e.parent&&(0,a.default)(f,c))return e.parent;try{for(var d=n((null===(u=e.parent)||void 0===u?void 0:u.childNodes)||[]),s=d.next();!s.done;s=d.next()){var v=s.value,p=(0,i.default)(v);if((0,a.default)(f,p))return v}}catch(e){r={error:e}}finally{try{s&&!s.done&&(o=d.return)&&o.call(d)}finally{if(r)throw r.error}}}},8732:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3942)),i=o(r(7790)),l=o(r(345)),u=function(e,t,r,o){var f,c;if(void 0===o&&(o=!1),!e)return!1;if(!o&&(0,a.default)(e))return!1;if(r(e,(0,l.default)(e,t)))return!0;try{for(var d=n((0,i.default)(e)),s=d.next();!s.done;s=d.next()){var v=s.value;if(u(v,e,r,o))return!0}}catch(e){f={error:e}}finally{try{s&&!s.done&&(c=d.return)&&c.call(d)}finally{if(f)throw f.error}}return!1};t.default=u},6016:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(2023)),a=n(r(4201)),i=["Cannot find ControlValueAccessor on the element.","If it is a mock input with [formControlName],","you need either to avoid mocking ReactiveFormsModule","or to avoid accessing the control in such a way,","because this tests ReactiveFormsModule instead of own implementation."].join(" ");t.default=function(e){var t=o.default&&(0,a.default)(o.default.NgControl,e.injector),r=null==t?void 0:t.valueAccessor;if(r)return r;var n=o.default&&(0,a.default)(o.default.FormControlDirective,e.injector);if(null==n?void 0:n.form)return n.form;var l=o.default&&(0,a.default)(o.default.NgModel,e.injector);if(l)return l;throw new Error(i)}},2298:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(2023)),i=r(7105),l=o(r(5006)),u=o(r(7331)),f=o(r(1991)),c=o(r(590)),d=o(r(5687)),s=o(r(4811)),v=o(r(589)),p=o(r(6016)),h=["onChange","onChangeCallback","onChangeCb","onChangeClb","onChangeFn","_onChange","_onChangeCallback","_onChangeCb","_onChangeClb","_onChangeFn","changeFn","_changeFn","onModelChange","cvaOnChange","cvaOnChangeCallback","cvaOnChangeCb","cvaOnChangeClb","cvaOnChangeFn","_cvaOnChange","_cvaOnChangeCallback","_cvaOnChangeCb","_cvaOnChangeClb","_cvaOnChangeFn"];t.default=function(e,t,r){var o,y,_=(0,c.default)((0,d.default)(),e,void 0);if(!_)throw new Error("Cannot find an element via ngMocks.change(".concat((0,s.default)(e),")"));var g=(0,p.default)(_);if(!function(e,t){return a.default&&e instanceof a.default.AbstractControl?(e.setValue(t),!0):a.default&&e instanceof a.default.NgModel?(e.update.emit(t),!0):!!(0,i.isMockControlValueAccessor)(e.instance)&&(e.instance.__simulateChange(t),!0)}(g,t)&&!function(e){return e.listeners.some((function(e){return"input"===e.name||"change"===e.name}))}(_)){try{for(var b=n(r?[r]:h),m=b.next();!m.done;m=b.next()){var M=m.value;if("function"==typeof g[M])return g.writeValue(t),void g[M](t)}}catch(e){o={error:e}}finally{try{m&&!m.done&&(y=b.return)&&y.call(b)}finally{if(o)throw o.error}}var k=(0,u.default)(g);throw new Error(["Unsupported type of ControlValueAccessor,","please ensure it has '".concat(r||"onChange","' method."),"If it is a 3rd-party library, please provide the correct name of the method in the 'methodName' parameter.","Possible Names: "+k.join(", ")+"."].join(" "))}!function(e,t){(0,f.default)(e,"focus");var r=Object.getOwnPropertyDescriptor(e.nativeElement,"value");(0,v.default)(e.nativeElement,"value",t),(0,f.default)(e,"input"),(0,f.default)(e,"change"),r&&((0,l.default)(e.nativeElement,"value",r),e.nativeElement.value=t),(0,f.default)(e,"blur")}(_,t)}},7655:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(2023)),i=r(7105),l=o(r(7331)),u=o(r(1991)),f=o(r(590)),c=o(r(5687)),d=o(r(4811)),s=o(r(6016)),v=["onTouched","onTouchedCallback","onTouchedCb","onTouchedClb","onTouchedFn","_onTouched","_onTouchedCallback","_onTouchedCb","_onTouchedClb","_onTouchedFn","markAsTouched","_markAsTouched","onModelTouched","cvaOnTouch","cvaOnTouchCallback","cvaOnTouchCb","cvaOnTouchClb","cvaOnTouchFn","_cvaOnTouch","_cvaOnTouchCallback","_cvaOnTouchCb","_cvaOnTouchClb","_cvaOnTouchFn"];t.default=function(e,t){var r,o,p=(0,f.default)((0,c.default)(),e,void 0);if(!p)throw new Error("Cannot find an element via ngMocks.touch(".concat((0,d.default)(e),")"));var h=(0,s.default)(p);if(!function(e){return a.default&&e instanceof a.default.AbstractControl?(e.markAsTouched(),!0):!!(0,i.isMockControlValueAccessor)(e.instance)&&(e.instance.__simulateTouch(),!0)}(h)&&!function(e){return e.listeners.some((function(e){return"focus"===e.name||"blur"===e.name}))}(p)){try{for(var y=n(t?[t]:v),_=y.next();!_.done;_=y.next()){var g=_.value;if("function"==typeof h[g])return void h[g]()}}catch(e){r={error:e}}finally{try{_&&!_.done&&(o=y.return)&&o.call(y)}finally{if(r)throw r.error}}var b=(0,l.default)(h);throw new Error(["Unsupported type of ControlValueAccessor,","please ensure it has '".concat(t||"onTouched","' method."),"If it is a 3rd-party library, please provide the correct name of the method in the 'methodName' parameter.","Possible Names: "+b.join(", ")+"."].join(" "))}!function(e){(0,u.default)(e,"focus"),(0,u.default)(e,"blur")}(p)}},9903:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(1991));t.default=function(e,t){(0,o.default)(e,"click",t)}},6843:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});for(var l=i(r(8195)),u=["focus","blur","load","unload","change","reset","scroll"],f="function"==typeof Event?function(e,t){return new CustomEvent(e,t)}:function(e,t){var r=n({bubbles:!1,cancelable:!1},t),o=document.createEvent("CustomEvent");return o.initCustomEvent(e,r.bubbles,r.cancelable,null),o},c={alt:{altKey:!0,code:"AltLeft",key:"Alt",location:1,which:18},arrowdown:{code:"ArrowDown",key:"ArrowDown",location:0,which:40},arrowleft:{code:"ArrowLeft",key:"ArrowLeft",location:0,which:37},arrowright:{code:"ArrowRight",key:"ArrowRight",location:0,which:39},arrowup:{code:"ArrowUp",key:"ArrowUp",location:0,which:38},backspace:{code:"Backspace",key:"Backspace",location:0,which:8},control:{code:"ControlLeft",ctrlKey:!0,key:"Control",location:1,which:17},enter:{code:"Enter",key:"Enter",location:0,which:13},esc:{code:"Escape",key:"Escape",location:0,which:27},meta:{code:"MetaLeft",key:"Meta",location:1,metaKey:!0,which:91},shift:{code:"ShiftLeft",key:"Shift",location:1,shiftKey:!0,which:16},space:{code:"Space",key:" ",location:0,which:32},tab:{code:"Tab",key:"Tab",location:0,which:9}},d=1;d<=12;d+=1)c["f".concat(d)]={code:"F".concat(d),key:"F".concat(d),location:0,which:d+111};t.default=function(e,t,r){var i=e.indexOf("."),d=a(-1===i?[e]:[e.slice(0,Math.max(0,i)),e.slice(i+1)],2),s=d[0],v=d[1],p=f(s,n({bubbles:-1===u.indexOf(e),cancelable:!0},t));return function(e,t){var r,n,a,i,u={};try{for(var f=o(t?t.split("."):[]),d=f.next();!d.done;d=f.next()){var s=d.value,v=c[s];if(v||1!==s.length||(v={code:(a=s,void 0,i=a.codePointAt(0),i&&i>=97&&i<=122||i&&i>=65&&i<=90?"Key".concat(a.toUpperCase()):i&&i>=48&&i<=57?"Digit".concat(a):"Unknown"),key:s}),!v)throw new Error("Unknown event part ".concat(s));(0,l.default)(u,v)}}catch(e){r={error:e}}finally{try{d&&!d.done&&(n=f.return)&&n.call(f)}finally{if(r)throw r.error}}t&&(0,l.default)(e,u)}(p,v),r&&(0,l.default)(p,r),p}},1991:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(590)),a=n(r(771)),i=n(r(4792)),l=n(r(4993)),u=n(r(5687)),f=n(r(8195)),c=n(r(6843)),d=["focus","blur","load","unload","change","reset","scroll"];t.default=function(e,t,r){var n,s=(n=(0,l.default)(e)?e:(0,o.default)((0,u.default)(),e,void 0),(0,a.default)(n)||(0,i.default)(n)?n.nativeElement:(0,l.default)(n)?n:void 0);if(!s)throw new Error("Cannot trigger ".concat("string"==typeof t?t:t.type," event undefined element"));if(!s.disabled){var v=function(e){return"string"==typeof e?(0,c.default)(e,{bubbles:-1===d.indexOf(e),cancelable:!0}):e}(t);v.target||(0,f.default)(v,{target:s}),r&&(0,f.default)(v,r),s.dispatchEvent(v)}}},343:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(5974);t.default=function(e){return"function"==typeof e||(0,n.isNgDef)(e,"t")}},4212:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=r(6739),l=r(5974),u=o(r(8344)),f=o(r(590)),c=o(r(1771)),d=o(r(5687)),s=o(r(951)),v=o(r(4811)),p=o(r(343)),h={};t.default=function(){for(var e=[],t=0;t0}),!0);else try{b.push((0,a.getInjection)(g))}catch(e){if(!e||"object"!=typeof e||void 0===e.ngTempTokenPath)throw e}if(b.length>0)return b[0];if(_!==h)return _;throw new Error("Cannot find an instance via ngMocks.findInstance(".concat((0,v.default)(y),")"))}},147:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(6456),l=r(6739),u=r(5974),f=a(r(8344)),c=a(r(7316)),d=a(r(1771)),s=a(r(5687)),v=a(r(951)),p=a(r(343));t.default=function(){for(var e,t,r=[],a=0;a0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(4792)),i=o(r(5687)),l=function(e){return(0,a.default)(e)?l(e.debugElement):e&&e.injector&&e.query?e:void 0};t.default=function(e,t){var r,o,a,u,f=t;return 3===e.length?(a=l(e[0]),u=e[1],f=e[2]):1===e.length?(a=l((0,i.default)()),u=n(e,1)[0]):e[0]?(a=l(e[0]))?u=e[1]:(a=l((0,i.default)()),u=(r=n(e,2))[0],f=r[1]):u=e[1],[a,u=null!==(o=l(u))&&void 0!==o?o:u,f]}},654:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(1165),o=r(6739);t.default=function(e){return Array.isArray(e)?n.By.css(1===e.length?"[".concat(e[0],"]"):"[".concat(e[0],'="').concat(e[1],'"]')):"string"==typeof e?n.By.css(e):n.By.directive((0,o.getSourceOfMock)(e))}},7316:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(771)),i=o(r(7453)),l=o(r(654));t.default=function(){for(var e=[],t=0;t0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(771)),i=o(r(4811)),l=o(r(7453)),u=o(r(654)),f={};t.default=function(){for(var e=[],t=0;t\\s+<","mg"),"><").replace(new RegExp('"\\s+>',"mg"),'">'):"";return r?n:n.trim()}var o;return(0,i.default)(t)?e(function(e,t){return t?e.outerHTML:e.innerHTML}(t,r)):(0,l.default)(t)?u(e,(0,a.default)(t).replace(new RegExp("&","mg"),"&").replace(new RegExp('"',"mg"),""").replace(new RegExp("<","mg"),"<").replace(new RegExp(">","mg"),">").replace(new RegExp("'","mg"),"'"),r):void 0};t.default=(0,o.default)(u)},9512:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4858)),a=n(r(9280)),i=n(r(4993)),l=n(r(8209)),u=function(e,t,r){if("string"==typeof t||void 0===t){var n=(o=t)?o.replace(new RegExp("\\s+","mg")," "):"";return r?n:n.trim()}var o;return(0,i.default)(t)?e(function(e,t){var r,n=null!==(r=e.textContent)&&void 0!==r?r:"";return t?n:n.trim()}(t,r)):(0,l.default)(t)?u(e,(0,a.default)(t),r):void 0};t.default=(0,o.default)(u)},3341:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t;return"#text"===(null===(t=e.nativeNode)||void 0===t?void 0:t.nodeName)&&e.parent?e.parent:e}},7717:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4201)),a=r(5974);t.default=function(e,t,r){if(t.injector&&"NullInjector"!==t.injector.constructor.name){var n=function(e){for(var t=e;"NullInjector"===(null==t?void 0:t.injector.constructor.name);)t=t.parent;if(t)return t.injector}(t.parent),i=n?(0,o.default)(r,n):void 0,l=(0,o.default)(r,t.injector);i!==l&&((0,a.isNgDef)(r,"t")&&void 0!==l||void 0!==l&&-1===e.indexOf(l))&&e.push(l)}}},2631:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(3341)),a=n(r(1484)),i=function(e){for(var t,r=e,n=null===(t=r.nativeNode)||void 0===t?void 0:t.__ngContext__;void 0===n&&r.parent;)n=(r=r.parent).nativeNode.__ngContext__;if("number"!=typeof n)return n;var o=r.injector._lView;return Array.isArray(o)?function(e,t){if("object"==typeof e[1]&&e[20]===t)return e;for(var r=21;r1&&f[1]&&"object"==typeof f[1]&&f[1].bindingStartIndex&&(v=f[1].bindingStartIndex);for(var p=0;p=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3341)),i=o(r(1484)),l=function(e){var t,r;if(!e||"object"!=typeof e)return e;try{for(var o=n(["renderElement","renderText","instance"]),a=o.next();!a.done;a=o.next()){var i=a.value;if(e[i])return e[i]}}catch(e){t={error:e}}finally{try{a&&!a.done&&(r=o.return)&&r.call(o)}finally{if(t)throw t.error}}return null};t.default=function(e,t,r){if(t&&t._debugContext){var n=(0,a.default)(t);(0,i.default)({el:n,nodes:t._debugContext.view.nodes,normalize:l,proto:r,result:e},!0)}}},1771:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(5974),a=n(r(7717)),i=n(r(2631)),l=n(r(2096));t.default=function(e,t,r){return(0,a.default)(e,t,r),(0,o.isNgDef)(r,"t")||"string"==typeof r||((0,l.default)(e,t,r),(0,i.default)(e,t,r)),e}},5687:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(2603);t.default=function(){var e=(0,n.getTestBed)()._activeFixtures;return e[e.length-1]}},4317:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8073));t.default=function(){var e;o.default.cacheDeclarations.clear(),null===(e=o.default.config.get("ngMocksDepsSkip"))||void 0===e||e.clear()}},4811:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(5974);t.default=function(e){return"string"==typeof e?e:"function"==typeof e?e.name:(0,n.isNgDef)(e,"t")?e._desc:Array.isArray(e)?e[0]:e?"":""}},951:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(771)),i=o(r(4792)),l=o(r(5687));t.default=function(e,t,r){var o,u,f,c,d,s,v=r;return 3===e.length?(c=(o=n(e,3))[0],d=o[1],v=o[2]):1===e.length?(c=(0,l.default)(),d=n(e,1)[0]):t(e[1])&&("string"==typeof(s=e[0])||Array.isArray(s)&&"string"==typeof s[0]||(0,i.default)(s)||(0,a.default)(s),1)?(c=(u=n(e,2))[0],d=u[1]):(c=(0,l.default)(),d=(f=n(e,2))[0],v=f[1]),[c,d,v]}},4641:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4201)),a=n(r(8862)),i=n(r(6297));t.default=function(e,t){if(e)try{var r=(0,i.default)(t);return function(e){try{return(0,a.default)(e)}catch(e){return}}((0,o.default)(r,e.injector).constructor)}catch(e){return}}},5018:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(1184)),l=a(r(590)),u=a(r(5687)),f=a(r(4641)),c=a(r(8027)),d={},s=function(e,t){var r=(0,i.default)(e),n=r.name,o=r.alias,a=void 0===o?"":o;if(!a&&n===t||a&&a===t)return n};t.default=function(e,t){for(var r=[],a=2;a=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3174));t.default=function(e,t){return function(){for(var r=[],o=0;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8073));t.default=function(e,t){var r,o,l=i.default.getConfigMock();try{for(var u=n((0,a.flatten)(e)),f=u.next();!f.done;f=u.next()){var c=f.value;t?l.set(c,t):l.delete(c)}}catch(e){r={error:e}}finally{try{f&&!f.done&&(o=u.return)&&o.call(u)}finally{if(r)throw r.error}}}},8909:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8073));t.default=function(e,t){var r,o,l=i.default.getOverrides();try{for(var u=n((0,a.flatten)(e)),f=u.next();!f.done;f=u.next()){var c=f.value;if(t){var d=l.has(c)?l.get(c):new Set;d.add(t),l.set(c,d)}else l.delete(c)}}catch(e){r={error:e}}finally{try{f&&!f.done&&(o=u.return)&&o.call(u)}finally{if(r)throw r.error}}}},1640:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(2603),i=o(r(3174)),l=o(r(8073)),u=l.default.global.get("faster-hooks")||{after:[],before:[]};l.default.global.set("faster-hooks",u);var f=function(e,t){return function(r){var o,f;if(a.TestBed.ngMocksFasterLock)return e.call(t,r);l.default.global.set("bullet:customized",!0);var c=e;try{for(var d=n(u.before),s=d.next();!s.done;s=d.next())c=(0,s.value)(c,t)}catch(e){o={error:e}}finally{try{s&&!s.done&&(f=d.return)&&f.call(d)}finally{if(o)throw o.error}}try{return(0,i.default)(a.TestBed,"ngMocksFasterLock",!0),c.call(t,r)}finally{(0,i.default)(a.TestBed,"ngMocksFasterLock",void 0)}}},c=function(e,t){return function(){var r,o;if(a.TestBed.ngMocksFasterLock)return e.call(t);if(l.default.global.has("bullet"))return l.default.global.has("bullet:customized")&&l.default.global.set("bullet:reset",!0),t;l.default.global.delete("bullet:customized"),l.default.global.delete("bullet:reset");var f=e;try{for(var c=n(u.after),d=c.next();!d.done;d=c.next())f=(0,d.value)(f,t)}catch(e){r={error:e}}finally{try{d&&!d.done&&(o=c.return)&&o.call(c)}finally{if(r)throw r.error}}try{return(0,i.default)(a.TestBed,"ngMocksFasterLock",!0),f.call(t)}finally{(0,i.default)(a.TestBed,"ngMocksFasterLock",void 0)}}};t.default=function(){a.TestBed.ngMocksFasterInstalled||(a.TestBed.configureTestingModule=f(a.TestBed.configureTestingModule,a.TestBed),a.TestBed.resetTestingModule=c(a.TestBed.resetTestingModule,a.TestBed),(0,i.default)(a.TestBed,"ngMocksFasterInstalled",!0));var e=(0,a.getTestBed)();return e.ngMocksFasterInstalled||(e.configureTestingModule=f(e.configureTestingModule,e),e.resetTestingModule=c(e.resetTestingModule,e),(0,i.default)(e,"ngMocksFasterInstalled",!0)),u}},7346:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(2603),a=n(r(8073)),i=n(r(1640)),l=n(r(2776)),u=function(e){var t,r=null!==(t=a.default.global.get("bullet:stack"))&&void 0!==t?t:[];r.push(e),a.default.global.set("bullet:stack",r),a.default.global.set("bullet:stack:id",e)},f=function(e){var t=a.default.global.get("bullet:stack");t.splice(t.indexOf(e),1),t.length>0?a.default.global.set("bullet:stack:id",t[t.length-1]):a.default.global.delete("bullet:stack:id"),function(e){for(var t=(0,o.getTestBed)()._activeFixtures||[],r=0,n=t.length-1;n>=0;n-=1)t[n].ngMocksStackId&&t[n].ngMocksStackId!==e?r+=1:(t[n].ngMocksStackId=void 0,t[n].destroy(),t.splice(n,1));0===r&&(0,l.default)()}(e)};t.default=function(){(0,i.default)();var e={},t={};beforeAll((function(){a.default.global.has("bullet:customized")&&o.TestBed.resetTestingModule(),a.default.global.set("bullet",!0),u(e)})),beforeEach((function(){u(t)})),afterEach((function(){f(t)})),afterAll((function(){f(e),a.default.global.delete("bullet"),a.default.global.has("bullet:reset")&&o.TestBed.resetTestingModule()}))}},2776:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(2603);t.default=function(){var e=(0,n.getTestBed)();e._instantiated=!1,e._moduleFactory=void 0,e._testModuleRef=null}},8027:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(2603),a=n(r(2970)),i=r(6739),l=n(r(345)),u=n(r(590)),f=n(r(1771)),c=n(r(5687)),d=n(r(4811)),s={};t.default=function(){for(var e=[],t=0;t0)return _[0];if(h){var _,g=(0,l.default)(h,void 0);if(g&&"#comment"===g.nativeNode.nodeName&&(_=(0,f.default)([],g,y)).length>0)return _[0]}if(p!==s)return p;throw new Error("Cannot find ".concat((0,a.default)(v)," instance via ngMocks.get"))}},4071:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(6259)),a=n(r(8073)),i=n(r(4317)),l=function(e){a.default.getDefaults().set(e,["exclude"])};t.default=function(e,t){void 0===t&&(t=!1),(0,i.default)(),l(e),t&&(0,o.default)(e,l)}},5228:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(6259)),a=n(r(8073)),i=n(r(4317)),l=function(e){a.default.getDefaults().set(e,["keep"])};t.default=function(e,t){void 0===t&&(t=!1),(0,i.default)(),l(e),t&&(0,o.default)(e,l)}},763:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(6259)),a=n(r(8073)),i=n(r(4317)),l=function(e){a.default.getDefaults().set(e,["mock"])};t.default=function(e,t){void 0===t&&(t=!1),(0,i.default)(),l(e),t&&(0,o.default)(e,l)}},3757:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(5974),a=n(r(8073)),i=n(r(4317));t.default=function(e,t){var r=!0;if(((0,o.isNgDef)(e,"m")&&(0,o.isNgDef)(t,"m")||(0,o.isNgDef)(e,"c")&&(0,o.isNgDef)(t,"c")||(0,o.isNgDef)(e,"d")&&(0,o.isNgDef)(t,"d")||(0,o.isNgDef)(e,"p")&&(0,o.isNgDef)(t,"p"))&&(r=!1),r)throw new Error("Cannot replace the declaration, both have to be a Module, a Component, a Directive or a Pipe");(0,i.default)(),a.default.getDefaults().set(e,["replace",t])}},4102:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(6259)),a=n(r(8073)),i=n(r(4317)),l=n(r(8909)),u=function(e){a.default.getDefaults().delete(e),(0,l.default)(e)};t.default=function(e,t){void 0===t&&(t=!1),(0,i.default)(),u(e),t&&(0,o.default)(e,u)}},7418:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(5551)),l=r(6456),u=a(r(1345)),f=a(r(6297)),c=r(5974),d=r(4152),s=r(3659),v=a(r(8073)),p=r(5395),h=r(8339),y=r(3821),_=r(2937),g=a(r(2415)),b=function(e,t,r){return!!t.has(e)||(t.add(e),r.has(e))},m=function(e,t,r,n,o){var a=e.get(t)||t;(0,c.isNgDef)(a,"m")?r.push(a):(0,c.isNgDef)(a,"c")||(0,c.isNgDef)(a,"d")?n.push(a):(0,c.isNgDef)(a,"p")?(n.push(a),o.push(a)):(0,d.isNgInjectionToken)(a)||o.push(a)},M=[["m","module"],["c","component"],["d","directive"],["p","pipe"]],k=function(e,t,r,n){b(t,e.skip,e.exclude)||n.push(e.keep.has(t)?t:r(t))},w=function(e,t){var r=e.skip,n=e.keep,o=e.providers,a=e.exclude,i=(0,f.default)(t);if(r.add(i),!a.has(i)){var l=n.has(i)?t:(0,g.default)(t);l&&o.push(l)}},O={component:p.MockComponent,directive:h.MockDirective,pipe:_.MockPipe},x=function(e,t,r){if(void 0===r&&(r=!0),t){var a,i=function(e,t){var r,a;if((0,s.isNgModuleDefWithProviders)(e))return"module-with-providers";try{for(var i=n(M),l=i.next();!l.done;l=i.next()){var u=o(l.value,2),f=u[0],d=u[1];if((0,c.isNgDef)(e,f))return"m"===f&&t.has(e)?"".concat(d,"-keep"):d}}catch(e){r={error:e}}finally{try{l&&!l.done&&(a=i.return)&&a.call(i)}finally{if(r)throw r.error}}return""}(t,e.keep);if("module-with-providers"!==i){var f=e.optional.get(t);f&&f!==t&&(a=f,e.keep.add(a))}a||(a=t),function(e,t,r,o){"module-with-providers"===t?function(e,t){e.skip.has(t.ngModule)||(e.skip.add(t.ngModule),e.exclude.has(t.ngModule)||e.imports.push(e.keep.has(t.ngModule)?t:(0,y.MockModule)(t)))}(e,r):"module-keep"===t||"module"===t&&o?k(e,r,y.MockModule,e.imports):"module"===t?function(e,t,r){var o,a,i,f;if(!b(t,e.skip,e.exclude)){var c=(0,u.default)(t);try{for(var d=n((0,l.flatten)([c.declarations,c.imports])),s=d.next();!s.done;s=d.next())r(e,h=s.value)}catch(e){o={error:e}}finally{try{s&&!s.done&&(a=d.return)&&a.call(d)}finally{if(o)throw o.error}}try{for(var v=n(c.providers?(0,l.flatten)(c.providers):[]),p=v.next();!p.done;p=v.next()){var h=p.value;w(e,h)}}catch(e){i={error:e}}finally{try{p&&!p.done&&(f=v.return)&&f.call(v)}finally{if(i)throw i.error}}}}(e,r,x):O[t]?k(e,r,O[t],e.declarations):w(e,r)}(e,i,a,r)}};t.default=function(e,t,r){var o,a,u,f,c,d;void 0===t&&(t=null),void 0===r&&(r=null);var s=function(e,t,r){var o=new Set((0,l.flatten)(e||[])),a=new Set((0,l.flatten)(t||[])),i=new Set((0,l.flatten)(r||[])),u=new Map;return function(e,t,r,o){var a,i;try{for(var u=n((0,l.mapKeys)(v.default.getDefaults())),f=u.next();!f.done;f=u.next()){var c=f.value,d=v.default.getBuildDeclaration(c);e.has(c)||t.has(c)||r.has(c)||(o.set(c,d),null===d?r.add(c):void 0===d?t.add(c):c===d&&e.add(c))}}catch(e){a={error:e}}finally{try{f&&!f.done&&(i=u.return)&&i.call(u)}finally{if(a)throw a.error}}}(o,a,i,u),{declarations:[],exclude:i,imports:[],keep:o,mock:a,optional:u,providers:[],skip:new Set}}(e,t,r),p=new Map;v.default.config.set("ngMocksDepsResolution",p);try{for(var h=n((0,l.mapValues)(s.keep)),y=h.next();!y.done;y=h.next()){var _=y.value;p.set(_,"keep")}}catch(e){o={error:e}}finally{try{y&&!y.done&&(a=h.return)&&a.call(h)}finally{if(o)throw o.error}}try{for(var g=n((0,l.mapValues)(s.exclude)),b=g.next();!b.done;b=g.next())_=b.value,p.set(_,"exclude")}catch(e){u={error:e}}finally{try{b&&!b.done&&(f=g.return)&&f.call(g)}finally{if(u)throw u.error}}v.default.config.set("mockNgDefResolver",new i.default);try{for(var M=n((0,l.mapValues)(s.mock)),k=M.next();!k.done;k=M.next()){var w=k.value;p.set(w,"mock"),s.optional.has(w)||x(s,w,!1)}}catch(e){c={error:e}}finally{try{k&&!k.done&&(d=M.return)&&d.call(M)}finally{if(c)throw c.error}}var O=function(e){var t,r,o=e.keep,a=e.skip,i=e.optional,l=e.exclude,u=e.imports,f=e.declarations,c=e.providers;try{for(var d=n(o),s=d.next();!s.done;s=d.next()){var v=s.value;a.has(v)||l.has(v)||i.has(v)||m(i,v,u,f,c)}}catch(e){t={error:e}}finally{try{s&&!s.done&&(r=d.return)&&r.call(d)}finally{if(t)throw t.error}}return{declarations:f,imports:u,providers:c}}(s);return v.default.config.delete("mockNgDefResolver"),v.default.config.delete("ngMocksDepsResolution"),O}},7411:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3295)),i=o(r(8073)),l=o(r(8344)),u=o(r(8458)),f=o(r(9400)),c=o(r(2298)),d=o(r(7655)),s=o(r(9903)),v=o(r(6843)),p=o(r(1991)),h=o(r(590)),y=o(r(7316)),_=o(r(4212)),g=o(r(147)),b=o(r(8062)),m=o(r(9512)),M=o(r(3539)),k=o(r(7977)),w=o(r(9311)),O=o(r(2237)),x=o(r(8909)),j=o(r(7346)),D=o(r(2776)),S=o(r(8027)),P=o(r(4071)),C=o(r(5228)),N=o(r(763)),T=o(r(3757)),E=o(r(4102)),A=o(r(7418)),R=o(r(7411)),I=o(r(5526)),V=o(r(2448)),B=o(r(8195)),F=o(r(589)),L=o(r(3048)),K=o(r(2120)),G=o(r(1368)),q=o(r(5535)),U=["onMockBuilderMissingDependency","onMockInstanceRestoreNeed","onTestBedFlushNeed"];t.default={autoSpy:M.default,change:c.default,click:s.default,config:function(e){var t,r,o=i.default.global.get("flags");try{for(var l=n(U),u=l.next();!u.done;u=l.next()){var f=u.value;null===e[f]?o[f]=a.default[f]:void 0!==e[f]&&(o[f]=e[f])}}catch(e){t={error:e}}finally{try{u&&!u.done&&(r=l.return)&&r.call(l)}finally{if(t)throw t.error}}null===e.mockRenderCacheSize?i.default.global.delete("mockRenderCacheSize"):void 0!==e.mockRenderCacheSize&&i.default.global.set("mockRenderCacheSize",e.mockRenderCacheSize)},crawl:l.default,defaultConfig:O.default,defaultMock:x.default,event:v.default,faster:j.default,find:h.default,findAll:y.default,findInstance:_.default,findInstances:g.default,findTemplateRef:G.default,findTemplateRefs:q.default,flushTestBed:D.default,formatHtml:b.default,formatText:m.default,get:S.default,globalExclude:P.default,globalKeep:C.default,globalMock:N.default,globalReplace:T.default,globalWipe:E.default,guts:A.default,hide:L.default,ignoreOnConsole:k.default,input:R.default,output:I.default,render:K.default,reset:V.default,reveal:u.default,revealAll:f.default,stub:B.default,stubMember:F.default,throwOnConsole:w.default,touch:d.default,trigger:p.default}},5526:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=i(r(7331)),u=i(r(7794)),f=i(r(5020)),c=i(r(445));t.default=function(e,t,r){var i,d;if("string"==typeof t)return c.default.mock(e,t,r);var s=e,v=t,p=["__zone_symbol__unconfigurables"];"function"==typeof t&&(s=c.default.createClone(t),v=e,p.push.apply(p,o([],n(Object.getOwnPropertyNames(s)),!1)));var h=o(o([],n((0,l.default)(v)),!1),n((0,u.default)(v)),!1);try{for(var y=a(h),_=y.next();!_.done;_=y.next()){var g=_.value,b=-1===p.indexOf(g)?(0,f.default)(v,g):void 0;b&&Object.prototype.hasOwnProperty.call(b,"value")&&void 0===b.value||c.default.definePropertyDescriptor(s,g,b)}}catch(e){i={error:e}}finally{try{_&&!_.done&&(d=y.return)&&d.call(y)}finally{if(i)throw i.error}}return s}},6189:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.ngMocks=void 0;var o=n(r(492));t.ngMocks=o.default},845:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(860),l=a(r(2137)),u=function(e,t,r){return!!e&&t instanceof i.TemplateRef&&r(t)},f=function(e,t,r){var a,c;if(!(0,l.default)(e))throw new Error("Only instances of mock declarations are accepted");if(function(e,t,r){return!!e.__template&&!!e.__vcr&&t(e.__template)&&r(e.__vcr,e.__template)}(e,t,r))return!0;try{for(var d=n(function(e){var t,r,o=[];try{for(var a=n(e.__ngMocksConfig.queryScanKeys||[]),l=a.next();!l.done;l=a.next())for(var u=l.value,f=e[u],c=e["__ngMocksVcr_".concat(u)],d=f instanceof i.QueryList?f.toArray():[f],s=c instanceof i.QueryList?c.toArray():[c],v=0;v0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(8732)),i=o(r(590)),l=o(r(5687)),u=o(r(951)),f=o(r(4811)),c=o(r(2789)),d=o(r(6703)),s=o(r(7845)),v={};t.default=function(){for(var e=[],t=0;t0)return y[0];if(h!==v)return h;throw new Error("Cannot find a TemplateRef via ngMocks.findTemplateRef(".concat((0,f.default)(p),")"))}},5535:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(8732)),i=o(r(590)),l=o(r(5687)),u=o(r(951)),f=o(r(2789)),c=o(r(6703)),d=o(r(7845));t.default=function(){for(var e=[],t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(8073)),l=a(r(589));t.default=function(e){var t,r,a=[],u=i.default.configInstance.get(e);if(null==u?void 0:u.overloads){var f=function(e,t,r){e?a.push((function(n){(0,l.default)(n,e,t,r)})):a.push(t)};try{for(var c=n(u.overloads),d=c.next();!d.done;d=c.next()){var s=o(d.value,3);f(s[0],s[1],s[2])}}catch(e){t={error:e}}finally{try{d&&!d.done&&(r=c.return)&&r.call(c)}finally{if(t)throw t.error}}}return a}},6691:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(2970)),i=o(r(8073));t.default=function(e){for(var t=[];e.length>0;){var r=n(e.pop()||[],2),o=r[0];r[1]===i.default.configInstance.get(o)&&t.push("function"==typeof o?(0,a.default)(o):o)}if(t.length>0){var l=i.default.global.get("flags"),u=["MockInstance: side effects have been detected (".concat(t.join(", "),")."),"Forgot to add MockInstance.scope() or to call MockInstance.restore()?"].join(" ");if("warn"===l.onMockInstanceRestoreNeed)console.warn(u);else if("throw"===l.onMockInstanceRestoreNeed)throw new Error(u)}}},3001:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockInstance=h,t.MockReset=function(){f.default.configInstance.clear()};var i,l=a(r(6804)),u=a(r(7592)),f=a(r(8073)),c=a(r(6691));u.default.subscribePush((function(e){i=e})),u.default.subscribePop((function(e,t){var r,a;try{for(var l=o(e.mockInstance||[]),u=l.next();!u.done;u=l.next()){var c=u.value;if(f.default.configInstance.has(c)){var d=f.default.configInstance.get(c);d.overloads.pop(),f.default.configInstance.set(c,n({},d))}}}catch(e){r={error:e}}finally{try{u&&!u.done&&(a=l.return)&&a.call(l)}finally{if(r)throw r.error}}i=t[t.length-1]}));var d=function(e){var t={};return"string"==typeof e[0]?(t.key=e[0],t.value=e[1],t.accessor=e[2]):(t.value=e[0],t.value&&"object"==typeof t.value&&(t.value=t.value.init)),t},s=[],v=!1;"undefined"!=typeof beforeEach&&(beforeEach((function(){return v=!0})),beforeEach((function(){return(0,c.default)(s)})),afterEach((function(){return v=!1})));var p=function(e,t,r,o){var a,l=f.default.configInstance.has(e)?f.default.configInstance.get(e):{},u=l.overloads||[];u.push([t,r,o]),l.overloads=u,f.default.configInstance.set(e,n({},l));var c=null!==(a=i.mockInstance)&&void 0!==a?a:[];return c.push(e),i.mockInstance=c,v&&s.push([e,f.default.configInstance.get(e),i]),r};function h(e){for(var t=[],r=1;r0){var o=d(t),a=o.key,u=o.value,c=o.accessor;return p(e,a,u,c)}var v=f.default.configInstance.get(e)||{};f.default.configInstance.set(e,n(n({},v),{overloads:[]}));for(var h=s.length-1;h>=0;h-=1)s[h][0]===e&&s[h][2]===i&&s.splice(h,1)}!function(e){e.remember=function(){u.default.stackPush()},e.restore=function(){u.default.stackPop()},e.scope=function(t){void 0===t&&(t="case"),"all"!==t&&"suite"!==t||(beforeAll(e.remember),afterAll(e.restore)),"all"!==t&&"case"!==t||(beforeEach(e.remember),afterEach(e.restore))}}(h||(t.MockInstance=h={}))},9988:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(6297)),l=r(5974),u=r(3659),f=a(r(8073)),c=r(5395),d=r(8339),s=r(2937),v=a(r(445)),p=r(3821),h=[["c",c.MockComponent],["d",d.MockDirective],["p",s.MockPipe]];t.default=function(e,t){var r=function(e,t){return function(r){var a;if(e.has(r))return function(e,t,r){var n=t.get(e);return e!==n&&r(),n}(r,e,t);var c=(0,i.default)(r);if(f.default.isExcludedDef(c))return function(e,t,r){t.set(e,void 0),r()}(r,e,t);f.default.touches.add(c);var d=function(e){var t,r;if((0,l.isNgDef)(e,"m")||(0,u.isNgModuleDefWithProviders)(e))return(0,p.MockModule)(e);if(f.default.hasBuildDeclaration(e))return f.default.getBuildDeclaration(e);if(f.default.flags.has("skipMock")&&"mock"!==f.default.getResolution(e))return e;try{for(var a=n(h),i=a.next();!i.done;i=a.next()){var c=o(i.value,2),d=c[0],s=c[1];if((0,l.isNgDef)(e,d))return s(e)}}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}}(r);return function(e,t){return(0,u.isNgModuleDefWithProviders)(t)&&(0,u.isNgModuleDefWithProviders)(e)}(r,d)&&e.set(r.ngModule,d.ngModule),f.default.flags.has("skipMock")&&(null===(a=f.default.config.get("ngMocksDepsSkip"))||void 0===a||a.add(d)),e.set(r,d),t(d!==r),d}}(t,e),a=function(e,t){return function(r){return v.default.resolveProvider(r,e,t)}}(t,e);return{resolve:r,resolveProvider:a}}},6860:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(6297)),l=o(r(7610));t.default=function(e){var t,r;try{for(var o=n((0,a.flatten)(null!=e?e:[])),u=o.next();!u.done;u=o.next()){var f=u.value,c=(0,i.default)(f);(0,l.default)(c)}}catch(e){t={error:e}}finally{try{u&&!u.done&&(r=o.return)&&r.call(o)}finally{if(t)throw t.error}}}},3821:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockModule=function(e){var t;(0,v.default)(e,"MockModule");var r=w(e),n=r.ngModule,o=r.ngModuleProviders,a=M(n);try{var i=x(n,O(n,a.isRootModule));g.default.flags.has("cacheModule")&&g.default.cacheDeclarations.set(n,i),g.default.flags.has("skipMock")&&(null===(t=g.default.config.get("ngMocksDepsSkip"))||void 0===t||t.add(i));var l=j(o);return D(e,n,o,i,l)}finally{k(a)}};var i=r(860),l=a(r(3295)),u=a(r(3174)),f=r(6456),c=a(r(1345)),d=a(r(615)),s=a(r(2970)),v=a(r(6804)),p=r(6763),h=r(5974),y=r(3659),_=r(1946),g=a(r(8073)),b=a(r(5937)),m=a(r(224)),M=function(e){var t=!1,r=!0;g.default.flags.has("hasRootModule")?r=!1:g.default.flags.add("hasRootModule");var n=g.default.getResolution(e);return function(e){return"mock"===e&&g.default.flags.has("skipMock")}(n)&&(t=!0,g.default.flags.delete("skipMock")),function(e){return-1!==l.default.neverMockModule.indexOf((0,s.default)(e))&&!g.default.flags.has("skipMock")}(e)&&(t=!0,g.default.flags.add("skipMock")),r||!function(e){return"keep"===e&&!g.default.flags.has("skipMock")}(n)&&!function(e){return"replace"===e&&!g.default.flags.has("skipMock")}(n)||(t=!0,g.default.flags.add("skipMock")),{isRootModule:r,toggleSkipMockFlag:t}},k=function(e){var t=e.isRootModule,r=e.toggleSkipMockFlag;r&&g.default.flags.has("skipMock")?g.default.flags.delete("skipMock"):r&&!g.default.flags.has("skipMock")&&g.default.flags.add("skipMock"),t&&g.default.flags.delete("hasRootModule")},w=function(e){var t,r;return(0,y.isNgModuleDefWithProviders)(e)?(t=e.ngModule,e.providers&&(r=e.providers)):t=e,{ngModule:t,ngModuleProviders:r}},O=function(e,t){var r;if((0,p.isMockNgDef)(e,"m"))return e;if(g.default.flags.has("cacheModule")&&g.default.cacheDeclarations.has(e))return(0,b.default)(e);if(!t&&"mock"!==(null===(r=g.default.config.get("ngMocksDepsResolution"))||void 0===r?void 0:r.get(e))&&g.default.hasBuildDeclaration(e)){var n=g.default.getBuildDeclaration(e);if((0,h.isNgDef)(n,"m")&&n!==e)return n}},x=function(e,t){var r=o(t?[!1]:(0,m.default)((0,c.default)(e),e),3),n=r[0],a=r[1],l=r[2];if(l&&(0,u.default)(e,"__ngMocksResolutions",l),n){var s=g.default.flags.has("skipMock")?e:_.Mock,v=(0,f.extendClass)(s);return(0,i.NgModule)(a)(v),(0,d.default)(v,e),v}return t||e},j=function(e){if(e){var t=o((0,m.default)({providers:e,skipExports:!0}),2),r=t[0],n=t[1];return r?n.providers:e}},D=function(e,t,r,o,a){return o===t&&a===r?e:(0,y.isNgModuleDefWithProviders)(e)?n({ngModule:o},a?{providers:a}:{}):o}},224:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=i(r(5551)),u=r(6456),f=i(r(6297)),c=i(r(8073)),d=i(r(7610)),s=i(r(9988)),v=i(r(6860)),p=function(e,t){return(0,u.flatten)(e).map(t).filter((function(e){return!!e}))},h=function(e,t,r,o){var a=c.default.config.get(o)||{},i=(0,f.default)(e),l=t(i);if(l){var u=c.default.config.get(i);if((null==u?void 0:u.export)&&o&&!a.export&&c.default.config.set(o,n(n({},a),{export:!0})),!r||a.exportAll||(null==u?void 0:u.export))return(0,d.default)(i,o),l}},y=function(e,t){return!e||!!t.exports&&-1!==t.exports.indexOf(e)};t.default=function(e,t){var r,i,_=c.default.config.has("mockNgDefResolver");_||c.default.config.set("mockNgDefResolver",new l.default),c.default.config.get("mockNgDefResolver").push();var g=!c.default.flags.has("skipMock"),b=function(e){void 0===e&&(e=!0),g=g||e},m=(0,s.default)(b,c.default.config.get("mockNgDefResolver")),M=m.resolve,k=function(e,t,r){var i,l,u,d={},s=function(e,t){return[["declarations",e],["hostDirectives",function(t){var r=(0,f.default)(t),o=e(r);return o===r?t:t==r?o:n(n({},t),{directive:o})}],["imports",e],["entryComponents",e],["bootstrap",e],["providers",t],["viewProviders",t],["exports",e],["schemas",function(e){return e}]]}(t,r),h=c.default.flags.has("cachePipe");h||c.default.flags.add("cachePipe");try{for(var y=o(s),_=y.next();!_.done;_=y.next()){var g=a(_.value,2),b=g[0],m=g[1];(null===(u=e[b])||void 0===u?void 0:u.length)&&(d[b]=p(e[b],m))}}catch(e){i={error:e}}finally{try{_&&!_.done&&(l=y.return)&&l.call(y)}finally{if(i)throw i.error}}return e.skipMarkProviders||((0,v.default)(d.providers),(0,v.default)(d.viewProviders)),h||c.default.flags.delete("cachePipe"),d}(e,M,m.resolveProvider);e.skipExports||function(e,t,r,n,a){var i,l,f=c.default.flags.has("skipMock")||c.default.flags.has("correctModuleExports");try{for(var d=o((0,u.flatten)([r.imports||[],r.declarations||[]])),s=d.next();!s.done;s=d.next()){var v=s.value,p=h(v,e,f,a);y(p,n)||(t(),n.exports=n.exports||[],n.exports.push(p))}}catch(e){i={error:e}}finally{try{s&&!s.done&&(l=d.return)&&l.call(d)}finally{if(i)throw i.error}}}(M,b,e,k,t);try{for(var w=o(t&&k.exports?(0,u.flatten)(k.exports):[]),O=w.next();!O.done;O=w.next()){var x=O.value;(0,d.default)(x,t)}}catch(e){r={error:e}}finally{try{O&&!O.done&&(i=w.return)&&i.call(w)}finally{if(r)throw r.error}}var j=c.default.config.get("mockNgDefResolver").pop();return _||c.default.config.delete("mockNgDefResolver"),[g,k,j]}},3877:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},2937:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockPipes=function(){for(var e=[],t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(8773)),i=o(r(1184)),l=r(5974),u=function(e,t){var r=" ".concat(function(e,t){return"i"===t?"[".concat(e,"]"):"(".concat(e,")")}(e,t),'="');return(r+="i"===t?e:"__ngMocksOutput('".concat(e,"', $event)"))+'"'},f=function(e,t,r){var o,a;if(!e&&"o"===r)return"";var l="",f=null!=e?e:t;try{for(var c=n(t),d=c.next();!d.done;d=c.next()){var s=d.value,v=(0,i.default)(s),p=v.name,h=v.alias;l+=-1===f.indexOf(h||p)?"":u(h||p,r)}}catch(e){o={error:e}}finally{try{d&&!d.done&&(a=c.return)&&a.call(c)}finally{if(o)throw o.error}}return l};t.default=function(e,t){var r=t.selector,n=t.bindings,o=t.inputs,i=t.outputs,u="";return"string"==typeof e?u=e:(0,l.isNgDef)(e,"p")&&n&&-1!==n.indexOf("$implicit")?u="{{ $implicit | ".concat((0,a.default)(e).name," }}"):r&&(u+="<".concat(r),u+=f(n,o,"i"),u+=f(n,i,"o"),u+=">")),u}},2091:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=i(r(3174)),u=i(r(5006)),f=i(r(445)),c=function(e,t,r){var n=function(){if("function"==typeof r[e]){if(t["__ngMocks_".concat(e,"__origin")]!==r[e]){var n=f.default.createClone(r[e],t,r);(0,l.default)(t,"__ngMocks_".concat(e),n),(0,l.default)(t,"__ngMocks_".concat(e,"__origin"),r[e])}return t["__ngMocks_".concat(e)]}return r[e]};return(0,l.default)(n,"__ngMocksProxy",!0),n},d=function(e,t,r){var n=function(n){t["__ngMocks_".concat(e)]&&(t["__ngMocks_".concat(e)]=void 0),t["__ngMocks_".concat(e,"__origin")]&&(t["__ngMocks_".concat(e,"__origin")]=void 0),r[e]=n};return(0,l.default)(n,"__ngMocksProxy",!0),n};t.default=function(e,t,r,i){var s,v;if(void 0===i&&(i=!1),t){(0,l.default)(e,"__ngMocks__source",t);var p,h=(p=e,o(o([],n(Object.getOwnPropertyNames(p)),!1),n(Object.keys(p)),!1)),y=o(o([],n(function(e){return o(o(o([],n(f.default.extractPropertiesFromPrototype(Object.getPrototypeOf(e))),!1),n(f.default.extractMethodsFromPrototype(Object.getPrototypeOf(e))),!1),n(Object.keys(e)),!1)}(t)),!1),n(r),!1);try{for(var _=a(y),g=_.next();!g.done;g=_.next()){var b=g.value;(i||-1===h.indexOf(b))&&((0,u.default)(e,b,{get:c(b,e,t),set:d(b,e,t)}),h.push(b))}}catch(e){s={error:e}}finally{try{g&&!g.done&&(v=_.return)&&v.call(_)}finally{if(s)throw s.error}}}}},6086:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},l=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var u=r(860),f=r(2603),c=l(r(3174)),d=r(6456),s=l(r(8862)),v=r(5974);t.default=function(e){var t,r,l;if(!(0,v.isNgDef)(e,"c")&&!(0,v.isNgDef)(e,"d"))return{};var p=(0,s.default)(e),h={};try{for(var y=i(Object.keys(p)),_=y.next();!_.done;_=y.next()){var g=_.value;"standalone"!==g?h[g]=p[g]:(0,c.default)(h,"__ngMocksStandalone",!!p[g])}}catch(e){t={error:e}}finally{try{_&&!_.done&&(r=y.return)&&r.call(y)}finally{if(t)throw t.error}}return h.selector&&/[\s,[\]]/.test(h.selector)&&(h.selector=""),h.selector||(h.selector=(null===(l=f.TestBed.ngMocksSelectors)||void 0===l?void 0:l.get(e))||"",h.selector||(h.selector="ng-mocks-".concat(e.name),function(e,t){var r,i=(0,d.extendClass)(e),l={provide:e,useExisting:i};t.providers=a(a([],o(t.providers||[]),!1),[l],!1);var c={};try{var s=f.TestBed.ngMocksOverrides.get(e).override;(c=n({},s.set)).providers=c.providers?a(a([],o(c.providers),!1),[l],!1):t.providers}catch(e){}var p=!0===t.__ngMocksStandalone;((0,v.isNgDef)(e,"c")?u.Component:u.Directive)(n(n(n({},t),c),p?{standalone:p}:{}))(i),f.TestBed.configureTestingModule(((r={})[p?"imports":"declarations"]=[i],r))}(e,h),f.TestBed.ngMocksSelectors&&f.TestBed.ngMocksSelectors.set(e,h.selector))),h}},8406:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockRenderFactory=function(e,t,r){void 0===r&&(r={}),(0,f.default)(e,"MockRender");var n="string"==typeof e||(0,c.isNgDef)(e,"t")?{}:(0,g.default)(e),o=(0,y.default)(e,n,t,r),a=m(o,t,e,r);return"root"!==d.default.current().level&&!1!==r.configureTestBed&&a.configureTestBed(),a};var o=r(860),a=r(2603),i=n(r(3174)),l=r(6456),u=n(r(2970)),f=n(r(6804)),c=r(5974),d=n(r(7592)),s=n(r(8073)),v=r(6189),p=n(r(5006)),h=r(5779),y=n(r(3078)),_=n(r(2091)),g=n(r(6086)),b=["Forgot to flush TestBed?","MockRender cannot be used without a reset after TestBed.get / TestBed.inject / TestBed.createComponent and another MockRender in the same test.","If you want to mock a service before rendering, consider usage of MockRenderFactory or MockInstance.","To flush TestBed, add a call of ngMocks.flushTestBed() before the call of MockRender, or pass `reset: true` to MockRender options."].join(" "),m=function(e,t,r,n){var f=function(n,d){f.configureTestBed();var y=a.TestBed.createComponent(e);return(0,_.default)(y.componentInstance,null!=n?n:{},null!=t?t:[]),(0,i.default)(y,"ngMocksStackId",s.default.global.get("bullet:stack:id")),(void 0===d||d)&&y.detectChanges(),"string"==typeof r||(0,c.isNgDef)(r,"c")||(0,c.isNgDef)(r,"d")||e.tpl&&(0,c.isNgDef)(r,"p")?function(e,t,r){e.point=e.debugElement.children[0]&&"#text"!==e.debugElement.children[0].nativeElement.nodeName&&"#comment"!==e.debugElement.children[0].nativeElement.nodeName?e.debugElement.children[0]:e.debugElement,(0,c.isNgDef)(t,"d")?(0,p.default)(e.point,"componentInstance",{get:function(){return v.ngMocks.get(e.point,t)}}):(0,c.isNgDef)(t,"p")&&(0,p.default)(e.point,"componentInstance",{get:function(){return v.ngMocks.findInstance(e.point,t)}}),function(e,t){if(e)try{t()}catch(e){}}(!r,(function(){return(0,_.default)(e.componentInstance,e.point.componentInstance,[])}))}(y,r,n):function(e,t,r){var n;try{n=(0,l.getInjection)(t)}catch(e){if((0,c.isNgDef)(t,"p"))throw new Error(["Cannot render ".concat((0,u.default)(t),"."),"Did you forget to set $implicit param, or add the pipe to providers?","https://ng-mocks.sudo.eu/guides/pipe"].join(" "));throw e}r&&v.ngMocks.stub(n,r),e.point=(0,h.MockService)(o.DebugElement,{childNodes:[],children:[],componentInstance:n,nativeElement:(0,h.MockService)(HTMLElement)}),(0,_.default)(e.componentInstance,e.point.componentInstance,[],!0)}(y,r,n),y};return f.declaration=e,f.bindings=t,f.configureTestBed=function(e,t){return function(){var r,n=(0,a.getTestBed)(),o=(null===(r=n._compiler)||void 0===r?void 0:r.declarations)||n.declarations||n._declarations;if(!o||-1===o.indexOf(e)){!function(e){var t=s.default.global.get("flags"),r=(0,a.getTestBed)();e.reset||!r._instantiated&&!r._testModuleRef?v.ngMocks.flushTestBed():"throw"!==t.onTestBedFlushNeed&&(r._instantiated||r._testModuleRef)&&("warn"===t.onTestBedFlushNeed&&console.warn(b),v.ngMocks.flushTestBed())}(t);try{var l=[];e.providers&&l.push(e.providers),l.push(e),a.TestBed.configureTestingModule({declarations:l})}catch(e){!function(e){var t=new Error(b);throw(0,i.default)(t,"parent",e),t}(e)}}}}(e,n),f}},1661:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0)return!0;var n=e.codePointAt(0);if(n&&n>=65&&n<=90&&null!==t.match(/\bthis\./gm))return!0;var o=new RegExp("\\(this,\\s*".concat(e,"\\)"),"mg");return null!==t.match(o)}(r[1],t,e)}},402:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return null!==e&&"object"==typeof e&&"InjectionToken"!==e.ngMetadataName&&"object"==typeof Object.getPrototypeOf(e)}},1365:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=i(r(5006)),u=i(r(7331)),f=i(r(7794)),c=i(r(5020));t.default=function(e,t,r,i){var d,s,v=function(){for(var n=[],o=0;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3174)),i=o(r(2970)),l=o(r(445));t.default=function(e){var t,r,o,u,f=(0,i.default)(e),c={};(0,a.default)(c,"__ngMocks",!0);var d=l.default.extractMethodsFromPrototype(e);try{for(var s=n(d),v=s.next();!v.done;v=s.next()){var p=v.value;l.default.mock(c,p,f)}}catch(e){t={error:e}}finally{try{v&&!v.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}var h=l.default.extractPropertiesFromPrototype(e);try{for(var y=n(h),_=y.next();!_.done;_=y.next()){var g=_.value;l.default.mock(c,g,"get",f),l.default.mock(c,g,"set",f)}}catch(e){o={error:e}}finally{try{_&&!_.done&&(u=y.return)&&u.call(y)}finally{if(o)throw o.error}}return Object.setPrototypeOf(c,e),c}},5006:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(2970)),i=["sanitize","bypassSecurityTrustHtml","bypassSecurityTrustStyle","bypassSecurityTrustScript","bypassSecurityTrustUrl","bypassSecurityTrustResourceUrl"],l={DomSanitizer:i,Sanitizer:i},u=function(e){var t,r,o,i=Object.getOwnPropertyNames(e);try{for(var u=n(null!==(o=l[(0,a.default)(e)])&&void 0!==o?o:[]),f=u.next();!f.done;f=u.next()){var c=f.value;i.push(c)}}catch(e){t={error:e}}finally{try{f&&!f.done&&(r=u.return)&&r.call(u)}finally{if(t)throw t.error}}return i};t.default=function(e){for(var t,r,o=[],a=e;a&&null!==Object.getPrototypeOf(a);){try{for(var i=(t=void 0,n(u(a))),l=i.next();!l.done;l=i.next()){var f=l.value;if("constructor"!==f){var c=Object.getOwnPropertyDescriptor(a,f);c&&(c.get||c.set)||-1!==o.indexOf(f)||o.push(f)}}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}a=Object.getPrototypeOf(a)}return o}},7794:function(e,t){var r=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){for(var t,n,o=[],a=e;a&&null!==Object.getPrototypeOf(a);){try{for(var i=(t=void 0,r(Object.getOwnPropertyNames(a))),l=i.next();!l.done;l=i.next()){var u=l.value;if("constructor"!==u){var f=Object.getOwnPropertyDescriptor(a,u);f&&(f.get||f.set)&&-1===o.indexOf(u)&&o.push(u)}}}catch(e){t={error:e}}finally{try{l&&!l.done&&(n=i.return)&&n.call(i)}finally{if(t)throw t.error}}a=Object.getPrototypeOf(a)}return o}},5020:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){for(var r=e;r&&null!==Object.getPrototypeOf(r);){var n=Object.getOwnPropertyDescriptor(r,t);if(n)return n;r=Object.getPrototypeOf(r)}}},8536:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(3174)),a=function(e,t){void 0===t&&(t=!1);var r,n,i=a.customMockFunction&&!t?a.customMockFunction(e):function(e){return n&&n(e),r};return(0,o.default)(i,"__ngMocks",!0),(0,o.default)(i,"__ngMocksSet",(function(e){return n=e})),(0,o.default)(i,"__ngMocksGet",(function(e){return r=e})),i};t.default=a},445:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.registerMockFunction=function(e){(0,o.default)().ngMockshelperMockService.registerMockFunction(e)};var o=n(r(1102)),a=n(r(1365)),i=n(r(3886)),l=n(r(5006)),u=n(r(7331)),f=n(r(7794)),c=n(r(5020)),d=n(r(4235)),s=n(r(8536)),v=n(r(2004)),p=n(r(3663)),h=n(r(9465));(0,o.default)().ngMockshelperMockService=(0,o.default)().ngMockshelperMockService||{mockFunction:s.default,registerMockFunction:function(e){(0,o.default)().ngMockshelperMockService.mockFunction.customMockFunction=e},createClone:a.default,createMockFromPrototype:i.default,definePropertyDescriptor:l.default,extractMethodsFromPrototype:u.default,extractPropertiesFromPrototype:f.default,extractPropertyDescriptor:c.default,mock:d.default,replaceWithMocks:v.default,resolveProvider:p.default,useFactory:h.default},t.default=(0,o.default)().ngMockshelperMockService},4235:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0&&"get"!==e[0]&&"set"!==e[0]?r=e[0]:e.length>0&&("get"===e[0]||"set"===e[0])&&(t=e[0],r=e[1]),{accessType:t,mockName:r}}(r),u=l.accessType,f=l.mockName,c=Object.getOwnPropertyDescriptor(e,t);if(c&&c[u||"value"])return c[u||"value"];var d=function(e,t,r,n){return"".concat(null!=t?t:"function"==typeof r.prototype?r.prototype.name:(0,a.default)(r),".").concat(e).concat(null!=n?n:"")}(t,f,e,u),s=i.default.mockFunction(d,!!u),v=function(e,t,r){var o;return n(n(n(n({},"get"===r&&e&&e.set?{set:e.set}:{}),"set"===r&&e&&e.get?{get:e.get}:{}),r?{}:{writable:!0}),((o={})[r||"value"]=t,o.configurable=!0,o.enumerable=!0,o))}(c,s,u);return v.get&&v.set&&v.get.__ngMocks&&v.set.__ngMocks&&v.set.__ngMocksSet((function(e){return v.get.__ngMocksGet(e)})),Object.defineProperty(e,t,v),s}},2004:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=r(1763),u=r(5974),f=i(r(8073)),c=["canActivate","canActivateChild","canDeactivate","canMatch","canLoad"],d=function(e,t){return Array.isArray(e[t])?function(e){var t,r,n=[];try{for(var a=o(e),i=a.next();!i.done;i=a.next()){var c=i.value;!f.default.isProvidedDef(c)&&f.default.isExcludedDef(l.NG_MOCKS_GUARDS)||(n.push(c),(0,u.isNgDef)(c)||f.default.touches.add(c))}}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}return n}(e[t]):e[t]},s=function(e,t){var r,i,v;if(f.default.cacheDeclarations.has(e))return f.default.cacheDeclarations.get(e);if("object"!=typeof e)return e;if(t.has(e))return e;var p=!1;return Array.isArray(e)?(r=a(function(e,t,r){var n,a,i=[],l=!1;e.set(t,i);try{for(var u=o(t),c=u.next();!c.done;c=u.next()){var d=c.value;f.default.isExcludedDef(d)?l=l||!0:(i.push(r(d,e)),l=l||i[i.length-1]!==d)}}catch(e){n={error:e}}finally{try{c&&!c.done&&(a=u.return)&&a.call(u)}finally{if(n)throw n.error}}return[l,i]}(t,e,s),2),p=r[0],v=r[1]):e&&(i=a(function(e,t,r){var a,i,s,v,p,h,y,_={},g=!1;e.set(t,_);try{for(var b=o(Object.keys(t)),m=b.next();!m.done;m=b.next()){var M=m.value;f.default.isExcludedDef(t[M])?g=g||!0:(_[M]=r(t[M],e),g=g||_[M]!==t[M])}}catch(e){a={error:e}}finally{try{m&&!m.done&&(i=b.return)&&i.call(b)}finally{if(a)throw a.error}}try{for(var k=o(c),w=k.next();!w.done;w=k.next()){var O=w.value,x=d(_,O);x&&_[O].length!==x.length&&(g=g||!0,_=n(n({},_),((p={})[O]=x,p)))}}catch(e){s={error:e}}finally{try{w&&!w.done&&(v=k.return)&&v.call(k)}finally{if(s)throw s.error}}if("object"==typeof _.resolve&&_.resolve){var j={},D=!1;try{for(var S=o(Object.keys(_.resolve)),P=S.next();!P.done;P=S.next()){M=P.value;var C=_.resolve[M];f.default.isProvidedDef(C)||!f.default.isExcludedDef(l.NG_MOCKS_RESOLVERS)?(j[M]=C,(0,u.isNgDef)(C)||f.default.touches.add(C)):D=D||!0}}catch(e){h={error:e}}finally{try{P&&!P.done&&(y=S.return)&&y.call(S)}finally{if(h)throw h.error}}D&&(g=g||!0,_=n(n({},_),{resolve:j}))}return[g,_]}(t,e,s),2),p=i[0],v=i[1]),p?(Object.setPrototypeOf(v,Object.getPrototypeOf(e)),v):e};t.default=function(e){var t=new Map,r=s(e,t);return t.clear(),r}},3663:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(6456),l=r(1763),u=a(r(7285)),f=a(r(6297)),c=r(4152),d=a(r(8073)),s=a(r(445)),v=a(r(2415)),p=function(e,t,r){var n=!1,a=!e;return t&&e&&!a&&(a=function(e,t){for(var r,n,a=[],i=2;i=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(3295)),l=a(r(6297)),u=r(4152),f=a(r(8073)),c=a(r(5006)),d=a(r(5020)),s=a(r(9465)),v=r(5779),p=i.default.neverMockProvidedFunction,h=i.default.neverMockToken,y=[[function(e){return"boolean"==typeof e},!1],[function(e){return"number"==typeof e},0],[function(e){return"string"==typeof e},""],[function(e){return null===e},null]],_=function(e,t,r){var a,i;return t===e?r?(0,s.default)(e,(function(){})):void 0:e.multi?void(null===(a=f.default.config.get("ngMocksMulti"))||void 0===a||a.add(t)):(-1!==Object.keys(e).indexOf("useValue")?i=function(e,t){return(0,s.default)(t,(function(){return e.useValue&&"object"==typeof e.useValue?(0,v.MockService)(e.useValue):function(e){var t,r;try{for(var a=n(y),i=a.next();!i.done;i=a.next()){var l=o(i.value,2),u=l[0],f=l[1];if(u(e))return f}}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}}(e.useValue)}))}(e,t):-1!==Object.keys(e).indexOf("useExisting")?i=e:-1!==Object.keys(e).indexOf("useClass")?i=function(e,t){return f.default.builtProviders.has(e.useClass)&&f.default.builtProviders.get(e.useClass)===e.useClass?e:(0,s.default)(t,(function(){return(0,v.MockService)(e.useClass)}))}(e,t):-1!==Object.keys(e).indexOf("useFactory")&&(i=(0,s.default)(t,(function(){return{}}))),i)};t.default=function(e,t){void 0===t&&(t=!1);var r=(0,l.default)(e);if("mock"===f.default.getResolution(r));else{if(function(e){return"function"==typeof e&&-1!==p.indexOf(e.name)}(r))return e;if(function(e){return(0,u.isNgInjectionToken)(e)&&-1!==h.indexOf(e.toString())}(r))return}var o=f.default.flags.has("cacheProvider")?f.default.cacheProviders:void 0;return r===e&&o&&o.has(r)?o.get(r):function(e,t,r){var o;return"function"==typeof t&&(o=function(e,t){return(0,s.default)(t,(function(){var r=(0,v.MockService)(t);return t!==e&&-1!==Object.keys(e).indexOf("useClass")&&function(e,t){var r,o,a=Object.getOwnPropertyNames(e),i=(0,v.MockService)(t);try{for(var l=n(Object.getOwnPropertyNames(i)),u=l.next();!u.done;u=l.next()){var f=u.value;if(-1===a.indexOf(f)){var s=(0,d.default)(i,f);(0,c.default)(e,f,s)}}}catch(e){r={error:e}}finally{try{u&&!u.done&&(o=l.return)&&o.call(l)}finally{if(r)throw r.error}}}(r,e.useClass),r}))}(e,t)),t===e&&o&&r&&r.set(t,o),o}(e,r,o)||_(e,r,t)}},5779:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockService=function(e){for(var t=[],r=1;r0&&"string"==typeof t[0]?t[0]:t[1],o=t.length>0&&t[0]&&"object"==typeof t[0]?t[0]:void 0,a=new Map,i=v(a,e,n,o);return a.clear(),i};var i=a(r(2970)),l=a(r(8195)),u=a(r(96)),f=a(r(2956)),c=a(r(402)),d=a(r(445)),s=[[u.default,function(e,t){var r=d.default.createMockFromPrototype(t.prototype);return e.set(t,r),r}],[f.default,function(e,t,r){var n=d.default.mockFunction("func:".concat(r||(0,i.default)(t)));return e.set(t,n()),n}],[function(e){return Array.isArray(e)},function(){return[]}],[c.default,function(e,t,r,o){var a,i,l=d.default.createMockFromPrototype(t.constructor.prototype);e.set(t,l);try{for(var u=n(Object.keys(t)),f=u.next();!f.done;f=u.next()){var c=f.value,s=o(e,t[c],"".concat(r||"instance",".").concat(c));void 0!==s&&(l[c]=s)}}catch(e){a={error:e}}finally{try{f&&!f.done&&(i=u.return)&&i.call(u)}finally{if(a)throw a.error}}return Object.setPrototypeOf(l,Object.getPrototypeOf(t)),l}]],v=function(e,t,r,a){void 0===r&&(r="");var i=function(e,t,r,a){var i,l,u;try{for(var f=n(s),c=f.next();!c.done;c=f.next()){var d=o(c.value,2),v=d[0],p=d[1];if(v(t))return null!==(u=e.get(t))&&void 0!==u?u:p(e,t,r,a)}}catch(e){i={error:e}}finally{try{c&&!c.done&&(l=f.return)&&l.call(f)}finally{if(i)throw i.error}}}(e,t,r,v);return a&&(0,l.default)(i,a),i}},534:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},9715:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(2023)),i=r(6456),l=o(r(7285)),u=o(r(6297)),f=r(1433),c=o(r(445)),d=o(r(7112)),s=o(r(3309)),v=function(e,t,r,n){var o=function(e,t){var r=(0,u.default)(t);return a.default.NG_VALIDATORS&&r===a.default.NG_VALIDATORS?(0,s.default)(r,(function(){return new f.MockValidatorProxy(e)})):a.default.NG_ASYNC_VALIDATORS&&r===a.default.NG_ASYNC_VALIDATORS?(0,s.default)(r,(function(){return new f.MockAsyncValidatorProxy(e)})):a.default.NG_VALUE_ACCESSOR&&r===a.default.NG_VALUE_ACCESSOR?(0,s.default)(r,(function(){return new f.MockControlValueAccessorProxy(e)})):void 0}(t,r);if(o)return o;var i=function(e,t,r){var n=(0,u.default)(r);if(n!==a.default.NgControl&&n!==a.default.FormControlDirective)return r!==n&&(0,l.default)(r.useExisting)===e?(0,d.default)(n,t):void 0}(e,t,r);return i||c.default.resolveProvider(r,n)};t.default=function(e,t,r,o){var l,f,c,d=[];try{for(var s=n((0,i.flatten)(r||[])),p=s.next();!p.done;p=s.next()){var h=p.value;(0,u.default)(h)===a.default.NG_VALUE_ACCESSOR&&(c=!1);var y=v(e,t,h,o);y&&d.push(y)}}catch(e){l={error:e}}finally{try{p&&!p.done&&(f=s.return)&&f.call(s)}finally{if(l)throw l.error}}return{providers:d,setControlValueAccessor:c}}},6847:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=i(r(5551)),u=i(r(5756)),f=i(r(615)),c=i(r(5555)),d=i(r(8385)),s=i(r(8073)),v=i(r(224)),p=i(r(445)),h=i(r(9715)),y=i(r(7112));t.default=function(e,t,r,i){var _,g,b,m,M=s.default.config.has("mockNgDefResolver");M||s.default.config.set("mockNgDefResolver",new l.default);var k=n({},i);if(void 0!==r.exportAs&&(k.exportAs=r.exportAs),void 0!==r.selector&&(k.selector=r.selector),void 0!==r.standalone&&(k.standalone=r.standalone),r.standalone&&r.imports){var w=o((0,v.default)({imports:r.imports,skipExports:!0}),2)[1].imports;(null==w?void 0:w.length)&&(k.imports=w)}if(r.hostDirectives){var O=o((0,v.default)({hostDirectives:r.hostDirectives,skipExports:!0}),2)[1].hostDirectives;(null==O?void 0:O.length)&&(k.hostDirectives=O)}var x=(0,h.default)(e,t,r.providers||[],s.default.config.get("mockNgDefResolver")),j=x.setControlValueAccessor,D=x.providers;D.push((0,y.default)(e,t)),k.providers=D;var S=(0,h.default)(e,t,r.viewProviders||[],s.default.config.get("mockNgDefResolver")).providers;S.length>0&&(k.viewProviders=S);var P=function(e,t,r){return{config:s.default.config.get(e),outputs:t.outputs,queryScanKeys:[],setControlValueAccessor:r}}(e,r,null!=j?j:-1!==p.default.extractMethodsFromPrototype(e.prototype).indexOf("writeValue"));(0,f.default)(t,e,P),r.queries&&(0,u.default)(t,r.inputs,Object.keys(r.queries)),(0,c.default)(t,r.outputs),P.queryScanKeys=(0,d.default)(t,r.queries),P.hostBindings=[];try{for(var C=a(r.hostBindings||[]),N=C.next();!N.done;N=C.next()){var T=o(N.value,1)[0];-1===P.hostBindings.indexOf(T)&&P.hostBindings.push(T)}}catch(e){_={error:e}}finally{try{N&&!N.done&&(g=C.return)&&g.call(C)}finally{if(_)throw _.error}}P.hostListeners=[];try{for(var E=a(r.hostListeners||[]),A=E.next();!A.done;A=E.next())T=o(A.value,1)[0],-1===P.hostListeners.indexOf(T)&&P.hostListeners.push(T)}catch(e){b={error:e}}finally{try{A&&!A.done&&(m=E.return)&&m.call(E)}finally{if(b)throw b.error}}return M||s.default.config.delete("mockNgDefResolver"),k}},6932:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(6456),a=n(r(6804)),i=r(6763),l=n(r(8073)),u=n(r(5937));t.default=function(e,t,r,n,f,c){if((0,a.default)(e,r),(0,i.isMockNgDef)(e,t))return e;if(l.default.flags.has(n)&&l.default.cacheDeclarations.has(e))return(0,u.default)(e);var d=l.default.config.has("ngMocksDepsResolution");d||l.default.config.set("ngMocksDepsResolution",new Map);var s=(0,o.extendClass)(f);return c(e,s),l.default.flags.has(n)&&l.default.cacheDeclarations.set(e,s),d||l.default.config.delete("ngMocksDepsResolution"),s}},7610:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(6739),a=n(r(8073));t.default=function(e,t){var r,n=(0,o.getSourceOfMock)(e),i=null!==(r=a.default.configInstance.get(n))&&void 0!==r?r:{__set:!0};i.exported||(i.exported=new Set),t&&i.exported.add((0,o.getSourceOfMock)(t)),i.__set&&(i.__set=void 0,a.default.configInstance.set(n,i))}},5937:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(1763),a=n(r(8073)),i=n(r(5687));t.default=function(e){var t;try{t=(0,i.default)().debugElement.injector.get(o.NG_MOCKS).get(e)}catch(e){}return t||(t=a.default.cacheDeclarations.get(e)),e.__ngMocksResolutions&&a.default.config.has("mockNgDefResolver")&&a.default.config.get("mockNgDefResolver").merge(e.__ngMocksResolutions),t}},7112:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){return{provide:e,useExisting:t}}},3309:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){return{multi:!0,provide:e,useFactory:t}}},4673:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=function(){function e(){this.stack=[],this.push()}return e.prototype.push=function(){this.stack.push(new Map)},e.prototype.pop=function(){var e;return null!==(e=this.stack.pop())&&void 0!==e?e:new Map},e.prototype.has=function(e){for(var t=this.stack.length-1;t>=0;t-=1)if(this.stack[t].has(e))return!0;return!1},e.prototype.get=function(e){for(var t=this.stack.length-1;t>=0;t-=1)if(this.stack[t].has(e))return this.stack[t].get(e)},e.prototype.set=function(e,t){for(var r=this.stack.length-1;r>=0;r-=1)this.stack[r].set(e,t);return this},e.prototype.merge=function(e){var t,r;try{for(var i=n((0,a.mapEntries)(e)),l=i.next();!l.done;l=i.next()){var u=o(l.value,2),f=u[0],c=u[1];this.set(f,c)}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}return this},e}();t.default=i},3174:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(445));t.default=function(e,t,r,n){void 0===n&&(n=!1),o.default.definePropertyDescriptor(e,t,{configurable:!0,enumerable:n,value:r,writable:!0})}},2023:function(e,t,r){var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,o)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),o=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),a=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)"default"!==r&&Object.prototype.hasOwnProperty.call(e,r)&&n(t,e,r);return o(t,e),t};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(7182)),l=i.AbstractControl,u=i.DefaultValueAccessor,f=i.FormControl,c=i.FormControlDirective,d=i.NG_ASYNC_VALIDATORS,s=i.NG_VALIDATORS,v=i.NG_VALUE_ACCESSOR,p=i.NgControl,h=i.NgModel;t.default={AbstractControl:l,DefaultValueAccessor:u,FormControl:f,FormControlDirective:c,NG_ASYNC_VALIDATORS:d,NG_VALIDATORS:s,NG_VALUE_ACCESSOR:v,NgControl:p,NgModel:h}},6456:function(__unused_webpack_module,exports,__webpack_require__){var __extends=this&&this.__extends||(extendStatics=function(e,t){return extendStatics=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},extendStatics(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}extendStatics(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),extendStatics,__values=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},__read=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},__spreadArray=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o0&&(0,core_define_property_1.default)(t,"parameters",__spreadArray([],__read(r),!1)),t};exports.extendClass=extendClass},4201:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(6456),o={};t.default=function(e,t){if(void 0===t&&(t=o),t===o)return(0,n.getTestBedInjection)(e);try{return t.get(e)}catch(e){return}}},4874:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8641)),a=n(r(1585)),i=n(r(2129));t.default=function(e){return function(t){(0,o.default)(t);try{return e(t)}catch(e){(0,a.default)(t),(0,i.default)(t)}}}},8862:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4673)),a=n(r(4874));t.default=function(e){return(0,a.default)((function(e){var t=(0,o.default)(e);if(t.Component)return t.Component;if(t.Directive)return t.Directive;throw new Error("Cannot resolve declarations")}))(e)}},1381:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8862)),a=n(r(1345)),i=r(5974);t.default=function(e){return(0,i.isNgDef)(e,"c")||(0,i.isNgDef)(e,"d")?(0,o.default)(e):(0,i.isNgDef)(e,"m")?(0,a.default)(e):void 0}},1345:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4673)),a=n(r(4874));t.default=function(e){return(0,a.default)((function(e){var t=(0,o.default)(e);if(t.NgModule)return t.NgModule;throw new Error("Cannot resolve declarations")}))(e)}},8749:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4673));t.default=function(e){var t;return null!==(t=(0,o.default)(e).parameters)&&void 0!==t?t:[]}},8773:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4673)),a=n(r(4874));t.default=function(e){return(0,a.default)((function(e){var t=(0,o.default)(e);if(t.Pipe)return t.Pipe;throw new Error("Cannot resolve declarations")}))(e)}},8911:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t,r,n;if(e&&("object"==typeof e||"function"==typeof e))return null!==(r=null===(t=e.ɵprov)||void 0===t?void 0:t.providedIn)&&void 0!==r?r:null===(n=e.ngInjectableDef)||void 0===n?void 0:n.providedIn}},1763:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.NG_MOCKS_ROOT_PROVIDERS=t.NG_MOCKS_INTERCEPTORS=t.NG_MOCKS_RESOLVERS=t.NG_MOCKS_GUARDS=t.NG_MOCKS_OVERRIDES=t.NG_MOCKS_TOUCHES=t.NG_MOCKS=void 0;var n=r(860);t.NG_MOCKS=new n.InjectionToken("NG_MOCKS"),t.NG_MOCKS.__ngMocksSkip=!0,t.NG_MOCKS_TOUCHES=new n.InjectionToken("NG_MOCKS_TOUCHES"),t.NG_MOCKS_TOUCHES.__ngMocksSkip=!0,t.NG_MOCKS_OVERRIDES=new n.InjectionToken("NG_MOCKS_OVERRIDES"),t.NG_MOCKS_OVERRIDES.__ngMocksSkip=!0,t.NG_MOCKS_GUARDS=new n.InjectionToken("NG_MOCKS_GUARDS"),t.NG_MOCKS_GUARDS.__ngMocksSkip=!0,t.NG_MOCKS_RESOLVERS=new n.InjectionToken("NG_MOCKS_RESOLVERS"),t.NG_MOCKS_RESOLVERS.__ngMocksSkip=!0,t.NG_MOCKS_INTERCEPTORS=new n.InjectionToken("NG_MOCKS_INTERCEPTORS"),t.NG_MOCKS_INTERCEPTORS.__ngMocksSkip=!0,t.NG_MOCKS_ROOT_PROVIDERS=new n.InjectionToken("NG_MOCKS_ROOT_PROVIDERS"),t.NG_MOCKS_ROOT_PROVIDERS.__ngMocksSkip=!0},5756:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(860),i=o(r(6439)),l=o(r(1184));t.default=function(e,t,r){var o,u;if(t)try{for(var f=n(t),c=f.next();!c.done;c=f.next()){var d=c.value,s=(0,l.default)(d),v=s.name,p=s.alias,h=s.required;r&&-1!==r.indexOf(v)||(0,a.Input)((0,i.default)({name:v,alias:p,required:h},!0))(e.prototype,v)}}catch(e){o={error:e}}finally{try{c&&!c.done&&(u=f.return)&&u.call(f)}finally{if(o)throw o.error}}}},615:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(860),i=o(r(6439)),l=o(r(1184));t.default=function(e,t){var r,o;if(t)try{for(var u=n(t),f=u.next();!f.done;f=u.next()){var c=f.value,d=(0,l.default)(c),s=d.name,v=d.alias,p=d.required;(0,a.Output)((0,i.default)({name:s,alias:v,required:p},!0))(e.prototype,s)}}catch(e){r={error:e}}finally{try{f&&!f.done&&(o=u.return)&&o.call(u)}finally{if(r)throw r.error}}}},8385:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0});var i=r(860),l={ContentChild:i.ContentChild,ContentChildren:i.ContentChildren,ViewChild:i.ViewChild,ViewChildren:i.ViewChildren},u=function(e){return 0===e.indexOf("__mock")},f=function(e){return n(n({},e),{ngMetadataName:e.ngMetadataName,read:i.ViewContainerRef})};t.default=function(e,t){var r,n;if(!t)return[];var i=a(function(e){var t,r,n=[],a=[];try{for(var i=o(Object.keys(e)),l=i.next();!l.done;l=i.next()){var c=l.value,d=e[c];n.push([c,d]),d.isViewQuery||u(c)||(a.push(c),n.push(["__ngMocksVcr_".concat(c),f(d)]))}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}return[n,a]}(t),2),c=i[0],d=i[1];try{for(var s=o(c),v=s.next();!v.done;v=s.next()){var p=a(v.value,2),h=p[0],y=p[1];y.ngMetadataName&&(0,l[y.ngMetadataName])(y.selector,y)(e.prototype,h)}}catch(e){r={error:e}}finally{try{v&&!v.done&&(n=s.return)&&n.call(s)}finally{if(r)throw r.error}}return d}},8641:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){if(!e)throw new Error(["undefined / null has been passed into ng-mocks as a declaration / provider.","Please ensure that the current test file has correct imports:","imported files exist and imported declarations have been exported in the file."].join(" "))}},1585:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(2970)),a=n(r(9628));t.default=function(e){if((0,a.default)(e))throw new Error(["ng-mocks got ".concat((0,o.default)(e)," which has been already mocked by jest.mock()."),"It is not possible to produce correct mocks for it, because jest.mock() removes Angular decorators.","To fix this, please avoid jest.mock() on the file which exports ".concat((0,o.default)(e)," or add jest.dontMock() on it."),"The same should be done for all related dependencies."].join(" "))}},2129:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(2970));t.default=function(e){throw new Error(["".concat((0,o.default)(e)," declaration has been passed into ng-mocks without Angular decorators."),"Therefore, it cannot be properly handled.","Highly likely,","undefined"==typeof jest?"":"jest.mock() has been used on its file, or","ng-mocks is imported in production code, or got a class without Angular decoration.","Otherwise, please create an issue on github: https://github.com/help-me-mom/ng-mocks/issues/new?title=False%20positive%20ng-mocks%20not%20in%20JIT.","Thank you in advance for support."].join(" "))}},6439:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){var r=e.name,n=e.alias,o=e.required;return void 0===t&&(t=!1),o?{name:r,alias:n,required:o}:n&&r!==n?t?n:"".concat(r,":").concat(n):t?"":r}},1184:function(e,t){var r=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){if("string"==typeof e){var t=r(e.split(":").map((function(e){return e.trim()})),2),n=t[0],o=t[1];return n!==o&&o?{name:n,alias:o}:{name:n}}return e}},5204:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.funcExtractDeps=void 0;var a=o(r(4673)),i=o(r(3295)),l=r(6456),u=r(9335),f=o(r(6297));t.funcExtractDeps=function(e,r,o){var c,d,s,v;void 0===o&&(o=!1);var p=(0,a.default)(e),h=(0,u.getNgType)(e);if(!h||"Injectable"===h)return r;var y=p[h];try{for(var _=n(i.default.dependencies),g=_.next();!g.done;g=_.next()){var b=g.value;if(y[b])try{for(var m=(s=void 0,n((0,l.flatten)(y[b]))),M=m.next();!M.done;M=m.next()){var k=M.value,w=(0,f.default)(k);r.has(w)||(r.add(w),o&&(0,t.funcExtractDeps)(w,r))}}catch(e){s={error:e}}finally{try{M&&!M.done&&(v=m.return)&&v.call(m)}finally{if(s)throw s.error}}}}catch(e){c={error:e}}finally{try{g&&!g.done&&(d=_.return)&&d.call(_)}finally{if(c)throw c.error}}return r}},7285:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return"function"==typeof e&&e.__forward_ref__?e():e}},1102:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return"undefined"==typeof window?r.g:window}},5590:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.getMockedNgDefOf=function(e,t){var r,n=null!==(r=e.mockOf)&&void 0!==r?r:e,u=(0,o.default)(a.NG_MOCKS),c=f(e,n,u);if(c&&!t)return c;if(c&&t&&(0,l.isMockedNgDefOf)(c,n,t))return c;throw new Error("There is no mock for ".concat((0,i.default)(n)))};var o=n(r(4201)),a=r(1763),i=n(r(2970)),l=r(732),u=n(r(8073)),f=function(e,t,r){if(r&&!r.has(t))throw new Error("There is no mock for ".concat((0,i.default)(t)));var n=r?r.get(t):void 0;return n===t&&(n=void 0),n||t===e?!n&&u.default.cacheDeclarations.has(t)&&(n=u.default.cacheDeclarations.get(t)):n=e,n}},2970:function(e,t){Object.defineProperty(t,"__esModule",{value:!0});var r=new RegExp("[^0-9a-z]+","mgi");t.default=function(e){var t;return"function"==typeof e&&e.name?t=e.name:"function"==typeof e?t="arrowFunction":"object"==typeof e&&e&&"InjectionToken"===e.ngMetadataName?t=e._desc:"object"==typeof e&&e&&"function"==typeof e.constructor&&(t=e.constructor.name),t||(t="unknown"),t.replace(r,"_")}},9335:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.getNgType=void 0;var o=n(r(4673)),a=r(4152);t.getNgType=function(e){if("string"!=typeof e){if((0,a.isNgInjectionToken)(e))return"Injectable";for(var t=(0,o.default)(e).decorators,r=t.length-1;r>=0;r-=1)if("Injectable"!==t[r])return t[r];return t.length>0?"Injectable":void 0}}},6739:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.getSourceOfMock=function(e){return"function"==typeof e&&e.mockOf?e.mockOf:e}},6297:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(3659);t.default=function(e){return e&&"object"==typeof e&&e.provide?e.provide:(0,n.isNgModuleDefWithProviders)(e)?e.ngModule:e&&"object"==typeof e&&e.directive?e.directive:e}},6804:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(2970)),a=r(5974);t.default=function(e,t){if(null==e)throw new Error("null / undefined has been passed into ".concat(t,". Please check that its import is correct."));if(!("MockPipe"===t&&(0,a.isNgDef)(e,"p")||"MockDirective"===t&&(0,a.isNgDef)(e,"d")||"MockComponent"===t&&(0,a.isNgDef)(e,"c")||"MockModule"===t&&(0,a.isNgDef)(e,"m"))){var r=function(e){return(0,a.isNgDef)(e,"p")?"pipe":(0,a.isNgDef)(e,"d")?"directive":(0,a.isNgDef)(e,"c")?"component":(0,a.isNgDef)(e,"m")?"module":(0,a.isNgDef)(e,"i")?"service":(0,a.isNgDef)(e,"t")?"token":""}(e);if(r&&"MockPipe"===t)throw new Error("".concat(t," accepts pipes, whereas ").concat((0,o.default)(e)," is a ").concat(r,"."));if(r&&"MockDirective"===t)throw new Error("".concat(t," accepts directives, whereas ").concat((0,o.default)(e)," is a ").concat(r,"."));if(r&&"MockComponent"===t)throw new Error("".concat(t," accepts components, whereas ").concat((0,o.default)(e)," is a ").concat(r,"."));if(r&&"MockModule"===t)throw new Error("".concat(t," accepts modules, whereas ").concat((0,o.default)(e)," is a ").concat(r,"."))}}},9628:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return!(!e||"function"!=typeof e&&"object"!=typeof e||!(e._isMockFunction&&e.mockName&&e.__annotations__))}},7105:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isMockControlValueAccessor=void 0;var o=n(r(2137));t.isMockControlValueAccessor=function(e){return!!(0,o.default)(e)&&!!e.__ngMocksConfig.isControlValueAccessor}},6763:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.isMockNgDef=function(e,t){return!!e.mockOf&&(!t||(0,n.isNgDef)(e.mockOf,t))};var n=r(5974)},7675:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isMockOf=function(e,t,r){return(0,o.default)(e)&&e.constructor===t&&(r?(0,a.isNgDef)(e.constructor,r):(0,a.isNgDef)(e.constructor))};var o=n(r(2137)),a=r(5974)},2650:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isMockValidator=void 0;var o=n(r(2137));t.isMockValidator=function(e){return!!(0,o.default)(e)&&!!e.__ngMocksConfig.isValidator}},2137:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return e&&"object"==typeof e&&!!e.__ngMocks}},732:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.isMockedNgDefOf=function(e,t,r){return"function"==typeof e&&e.mockOf===t&&(!r||(0,n.isNgDef)(e,r))};var n=r(5974)},5974:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0}),t.isNgDef=function(e,t){if("t"===t)return(0,n.isNgInjectionToken)(e);if("function"!=typeof e)return!1;var r=a(e,t),o=i(e,t),c=l(e,t),d=u(e,t),s=f(e,t);return r||o||c||d||s};var n=r(4152),o=r(7297),a=function(e,t){return(!t||"m"===t)&&(0,o.isNgType)(e,"NgModule")},i=function(e,t){return(!t||"c"===t)&&(0,o.isNgType)(e,"Component")},l=function(e,t){return(!t||"d"===t)&&(0,o.isNgType)(e,"Directive")},u=function(e,t){return(!t||"p"===t)&&(0,o.isNgType)(e,"Pipe")},f=function(e,t){return(!t||"i"===t)&&(0,o.isNgType)(e,"Injectable")}},4152:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.isNgInjectionToken=void 0,t.isNgInjectionToken=function(e){return e&&"object"==typeof e&&"InjectionToken"===e.ngMetadataName}},3659:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.isNgModuleDefWithProviders=void 0,t.isNgModuleDefWithProviders=function(e){return e&&"object"==typeof e&&"function"==typeof e.ngModule}},7297:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isNgType=void 0;var o=n(r(4673));t.isNgType=function(e,t){var r=(0,o.default)(e).decorators;if(0===r.length)return!1;var n=1;if("Injectable"===t&&-1!==r.indexOf("Injectable"))return!0;for(;"Injectable"===r[r.length-n];)n+=1;return r[r.length-n]===t}},6580:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.isStandalone=function(e){var t=(0,a.getNgType)(e);return"NgModule"!==t};var o=n(r(4673)),a=r(9335)},6259:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(4673)),i=o(r(3295)),l=r(6456),u=o(r(6297)),f=function(e,t,r){var o,c,d,s,v,p;void 0===r&&(r=new Set);var h=(0,a.default)(e);try{for(var y=n(h.decorators),_=y.next();!_.done;_=y.next()){var g=_.value;try{for(var b=(d=void 0,n(i.default.dependencies)),m=b.next();!m.done;m=b.next()){var M=m.value;if(h[g][M])try{for(var k=(v=void 0,n((0,l.flatten)(h[g][M]))),w=k.next();!w.done;w=k.next()){var O=w.value,x=(0,u.default)(O);x&&!r.has(x)&&(r.add(x),t(x),f(x,t,r))}}catch(e){v={error:e}}finally{try{w&&!w.done&&(p=k.return)&&p.call(k)}finally{if(v)throw v.error}}}}catch(e){d={error:e}}finally{try{m&&!m.done&&(s=b.return)&&s.call(b)}finally{if(d)throw d.error}}}}catch(e){o={error:e}}finally{try{_&&!_.done&&(c=y.return)&&c.call(y)}finally{if(o)throw o.error}}};t.default=f},1433:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.MockAsyncValidatorProxy=t.MockValidatorProxy=t.MockControlValueAccessorProxy=void 0;var r=function(e,t,r,n){if(e.instance&&n&&(e.instance[n]=r),e.instance&&e.instance[t])return e.instance[t](r)},n=function(){function e(e){this.target=e}return e.prototype.registerOnChange=function(e){r(this,"registerOnChange",e,"__simulateChange")},e.prototype.registerOnTouched=function(e){r(this,"registerOnTouched",e,"__simulateTouch")},e.prototype.setDisabledState=function(e){r(this,"setDisabledState",e)},e.prototype.writeValue=function(e){r(this,"writeValue",e)},e}();t.MockControlValueAccessorProxy=n;var o=function(){function e(e){this.target=e}return e.prototype.registerOnValidatorChange=function(e){r(this,"registerOnValidatorChange",e,"__simulateValidatorChange")},e.prototype.validate=function(e){return this.instance&&this.instance.validate?this.instance.validate(e):null},e}();t.MockValidatorProxy=o;var a=function(){function e(e){this.target=e}return e.prototype.registerOnValidatorChange=function(e){r(this,"registerOnValidatorChange",e,"__simulateValidatorChange")},e.prototype.validate=function(e){if(this.instance&&this.instance.validate){var t=this.instance.validate(e);return void 0===t?Promise.resolve(null):t}return Promise.resolve(null)},e}();t.MockAsyncValidatorProxy=a},5980:function(e,t,r){var n,o=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)});Object.defineProperty(t,"__esModule",{value:!0}),t.LegacyControlValueAccessor=void 0;var a=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return o(t,e),t.prototype.__simulateChange=function(){},t.prototype.__simulateTouch=function(){},t.prototype.__simulateValidatorChange=function(){},t}(r(1946).Mock);t.LegacyControlValueAccessor=a},1946:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o0&&((o=(0,c.default)(e._providers||(null===(n=e._compiler)||void 0===n?void 0:n.providers)).touches)||(o=new Set,t.providers=t.providers||[],t.providers.push({provide:O.NG_MOCKS_TOUCHES,useValue:o})),T(t,o)),o}(Y,K,J.touches);return Z&&v.ngMocks.flushTestBed(),ee&&function(e){var t,r;try{for(var i=o((0,b.mapEntries)(e)),l=i.next();!l.done;l=i.next()){var u=a(l.value,2),c=u[0],d=a(u[1],2),s=d[0],v=d[1];f.TestBed.ngMocksOverrides.set(c,n(n({},v),{override:s})),N(c,s)}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}}(ee),!te||Y._instantiated||Y._testModuleRef||function(e,t){var r,n;if(f.TestBed.ngMocksOverrides){var i=C.default.touches;C.default.touches=t;try{for(var l=o((0,b.flatten)(e.ngModule||[])),u=l.next();!u.done;u=l.next()){var c=u.value;E(c)}}catch(e){r={error:e}}finally{try{u&&!u.done&&(n=l.return)&&n.call(l)}finally{if(r)throw r.error}}!function(e){var t,r;try{for(var n=o((0,b.mapEntries)(C.default.getDefaults())),i=n.next();!i.done;i=n.next()){var l=a(i.value,2),u=l[0];"mock"===a(l[1],1)[0]&&((0,S.isNgDef)(u,"i")||(0,S.isNgDef)(u,"t"))&&(e.has(u)||f.TestBed.ngMocksOverrides.has(u)||A(u,e))}}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}}(t),C.default.touches=i}}(Y,te),e.call(t,K)}},I=function(e,t){return function(){return C.default.global.delete("builder:config"),C.default.global.delete("builder:module"),f.TestBed.ngMocksSelectors=void 0,function(e){var t,r,n;if(null===(n=e.ngMocksOverrides)||void 0===n?void 0:n.size){v.ngMocks.flushTestBed();try{for(var i=o((0,b.mapEntries)(e.ngMocksOverrides)),l=i.next();!l.done;l=i.next()){var u=a(l.value,2),f=u[0],c=u[1];N(f,c)}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}}e.ngMocksOverrides=void 0}(f.TestBed),e.call(t)}},V=function(e){if(!u.ViewContainerRef.ngMocksOverridesPatched&&((0,g.default)(u.ViewContainerRef,"ngMocksOverridesPatched",!0),e.createComponent)){var t=e.createComponent,r=(0,y.default)(t,void 0,void 0,(function(e){for(var r,n=[],o=1;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},l=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var u=l(r(8073)),f={id:{},level:"root"},c=u.default.global.get("reporter-stack")||[a({},f)];u.default.global.set("reporter-stack",c);var d=null!==(n=u.default.global.get("reporter-stack-push"))&&void 0!==n?n:[];u.default.global.set("reporter-stack-push",d);var s=null!==(o=u.default.global.get("reporter-stack-pop"))&&void 0!==o?o:[];u.default.global.set("reporter-stack-pop",s),t.default={current:function(){return c[c.length-1]},stackPop:function(){var e,t,r=c.pop();if(0===c.length&&c.push("root"===(null==r?void 0:r.level)?r:a({},f)),r&&"root"!==r.level)try{for(var n=i(s),o=n.next();!o.done;o=n.next())(0,o.value)(r,c)}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=n.return)&&t.call(n)}finally{if(e)throw e.error}}u.default.global.set("reporter-stack-id",c[c.length-1].id)},stackPush:function(){var e,t,r={};u.default.global.set("reporter-stack-id",r);var n={id:r,level:"runtime"};c.push(n);try{for(var o=i(d),a=o.next();!a.done;a=o.next())(0,a.value)(n,c)}catch(t){e={error:t}}finally{try{a&&!a.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}},subscribePop:function(e){-1===s.indexOf(e)&&s.push(e)},subscribePush:function(e){d.indexOf(e)&&d.push(e),c.length>0&&e(c[c.length-1],c)},unsubscribePop:function(e){var t=s.indexOf(e);-1!==t&&s.splice(t,1)},unsubscribePush:function(e){var t=d.indexOf(e);-1!==t&&d.splice(t,1)}}},8073:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3295)),i=o(r(1102)),l=o(r(2970)),u=function(e){return function(){return f.global.has(e)||f.global.set(e,new Map),f.global.get(e)}};(0,i.default)().ngMocksUniverse=(0,i.default)().ngMocksUniverse||{};var f=(0,i.default)().ngMocksUniverse;f.builtDeclarations=new Map,f.builtProviders=new Map,f.cacheDeclarations=new Map,f.cacheProviders=new Map,f.config=new Map,f.configInstance=new Map,f.flags=new Set(a.default.flags),f.global=new Map,f.touches=new Set,f.global.set("flags",{onMockBuilderMissingDependency:a.default.onMockBuilderMissingDependency,onMockInstanceRestoreNeed:a.default.onMockInstanceRestoreNeed,onTestBedFlushNeed:a.default.onTestBedFlushNeed}),f.getOverrides=u("overrides"),f.getDefaults=u("defaults"),f.getConfigMock=u("configMock");var c=function(e){var t;return(t=f.getDefaults().get(e))||(t="function"==typeof e?f.getDefaults().get("@".concat((0,l.default)(e))):void 0)?t:[]};f.getResolution=function(e){var t=f.config.get("ngMocksDepsResolution");return(null==t?void 0:t.has(e))?t.get(e):n(c(e),1)[0]},f.getBuildDeclaration=function(e){if(f.builtDeclarations.has(e))return f.builtDeclarations.get(e);var t=n(c(e),2),r=t[0],o=t[1];return"exclude"===r?null:r&&"keep"!==r?"replace"===r?o:void 0:e},f.hasBuildDeclaration=function(e){if(f.builtDeclarations.has(e))return void 0!==f.builtDeclarations.get(e);var t=n(c(e),1)[0];return!!t&&"mock"!==t};var d=function(e){return f.hasBuildDeclaration(e)},s=function(e){return f.getBuildDeclaration(e)};f.isExcludedDef=function(e){var t=f.getResolution(e);return(!t||"exclude"===t)&&d(e)&&null===s(e)},f.isProvidedDef=function(e){return d(e)&&null!==s(e)},f.getDefaults().set("@StoreDevtoolsModule",["exclude"]),f.indexValue=0,f.index=function(){return f.indexValue++},t.default=f},4117:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0});var o=r(6456),a=r(1763);t.default=function(e){var t,r,i,l,u;try{for(var f=n((0,o.flatten)(e||[])),c=f.next();!c.done;c=f.next()){var d=c.value;"object"==typeof d&&(d.provide===a.NG_MOCKS&&(i=d.useValue),d.provide===a.NG_MOCKS_OVERRIDES&&(l=d.useValue),d.provide===a.NG_MOCKS_TOUCHES&&(u=d.useValue))}}catch(e){t={error:e}}finally{try{c&&!c.done&&(r=f.return)&&r.call(f)}finally{if(t)throw t.error}}return{mocks:i,overrides:l,touches:u}}},9539:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockBuilderStash=void 0;var a=o(r(3295)),i=o(r(8073)),l=function(){function e(){this.data={}}return e.prototype.backup=function(){this.data={builtDeclarations:i.default.builtDeclarations,builtProviders:i.default.builtProviders,cacheDeclarations:i.default.cacheDeclarations,cacheProviders:i.default.cacheProviders,config:i.default.config,configInstance:i.default.configInstance,flags:i.default.flags,touches:i.default.touches},i.default.builtDeclarations=new Map,i.default.builtProviders=new Map,i.default.cacheDeclarations=new Map,i.default.cacheProviders=new Map,i.default.config=new Map,i.default.configInstance=new Map,i.default.flags=new Set(a.default.flags),i.default.touches=new Set},e.prototype.restore=function(){var e,t;try{for(var r=n(Object.keys(this.data)),o=r.next();!o.done;o=r.next()){var a=o.value;i.default[a]=this.data[a]}}catch(t){e={error:t}}finally{try{o&&!o.done&&(t=r.return)&&t.call(r)}finally{if(e)throw e.error}}},e}();t.MockBuilderStash=l},9755:function(e,t,r){var n,o=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),a=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(o,a){function i(e){try{u(n.next(e))}catch(e){a(e)}}function l(e){try{u(n.throw(e))}catch(e){a(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(i,l)}u((n=n.apply(e,t||[])).next())}))},i=this&&this.__generator||function(e,t){var r,n,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:l(0),throw:l(1),return:l(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function l(l){return function(u){return function(l){if(r)throw new TypeError("Generator is already executing.");for(;a&&(a=0,l[0]&&(i=0)),i;)try{if(r=1,n&&(o=2&l[0]?n.return:l[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,l[1])).done)return o;switch(n=0,o&&(l=[2&l[0],o.value]),l[0]){case 0:case 1:o=l;break;case 4:return i.label++,{value:l[1],done:!1};case 5:i.label++,n=l[1],l=[0];continue;case 7:l=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==l[0]&&2!==l[0])){i=0;continue}if(3===l[0]&&(!o||l[1]>o[0]&&l[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},u=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockBuilderPerformance=void 0;var f=r(2603),c=r(6456),d=u(r(8073)),s=r(9620),v=u(r(2739)),p=u(r(9049)),h=u(r(7570)),y=u(r(1271)),_=u(r(5633)),g=u(r(4503)),b=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return o(t,e),t.prototype.build=function(){var t=d.default.global;if(t.has("builder:module")&&t.has("builder:config")&&this.equalsTo(t.get("builder:config")))return(0,g.default)(t.get("builder:module"));t.has("builder:module")&&t.delete(t.get("builder:module"));var r=this.cloneConfig(),n=e.prototype.build.call(this);return t.set("builder:config",r),t.set("builder:module",n),(0,g.default)(n)},t.prototype.then=function(t,r){return a(this,void 0,Promise,(function(){var n,o;return i(this,(function(a){return(n=d.default.global).has("bullet")&&n.has("builder:module")&&n.has("builder:config")&&this.equalsTo(n.get("builder:config"))?[2,n.get(n.get("builder:module")).then(t,r)]:(n.has("bullet")&&n.has("bullet:reset")&&(console.warn("ngMocks.faster has zero effect due to changes in testing module between runs"),n.delete("bullet"),f.TestBed.resetTestingModule(),n.set("bullet",!0)),o=e.prototype.then.call(this,t,r),n.set(n.get("builder:module"),o),[2,o])}))}))},t.prototype.cloneConfig=function(){var e=(0,_.default)();return(0,c.mapValues)(this.beforeCC,e.beforeCC),(0,c.mapValues)(this.excludeDef,e.excludeDef),(0,c.mapValues)(this.keepDef,e.keepDef),(0,c.mapValues)(this.mockDef,e.mockDef),(0,c.mapValues)(this.replaceDef,e.replaceDef),(0,c.mapEntries)(this.configDef,e.configDef),(0,c.mapEntries)(this.defProviders,e.defProviders),(0,c.mapEntries)(this.defValue,e.defValue),(0,c.mapEntries)(this.providerDef,e.providerDef),e},t.prototype.equalsTo=function(e){var t,r,n,o,a,i;try{for(var u=l(["beforeCC","keepDef","replaceDef","excludeDef","mockDef"]),f=u.next();!f.done;f=u.next()){var c=f.value;if(!(0,y.default)(this[c],e[c]))return!1}}catch(e){t={error:e}}finally{try{f&&!f.done&&(r=u.return)&&r.call(u)}finally{if(t)throw t.error}}try{for(var d=l(["defValue"]),s=d.next();!s.done;s=d.next())if(c=s.value,!(0,p.default)(this[c],e[c]))return!1}catch(e){n={error:e}}finally{try{s&&!s.done&&(o=d.return)&&o.call(d)}finally{if(n)throw n.error}}try{for(var _=l(["providerDef","defProviders"]),g=_.next();!g.done;g=_.next())if(c=g.value,!(0,p.default)(this[c],e[c],h.default))return!1}catch(e){a={error:e}}finally{try{g&&!g.done&&(i=_.return)&&i.call(_)}finally{if(a)throw a.error}}return(0,p.default)(this.configDef,e.configDef,v.default)},t}(s.MockBuilderPromise);t.MockBuilderPerformance=b},9620:function(e,t,r){var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(o,a){function i(e){try{u(n.next(e))}catch(e){a(e)}}function l(e){try{u(n.throw(e))}catch(e){a(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(i,l)}u((n=n.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var r,n,o,a,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return a={next:l(0),throw:l(1),return:l(2)},"function"==typeof Symbol&&(a[Symbol.iterator]=function(){return this}),a;function l(l){return function(u){return function(l){if(r)throw new TypeError("Generator is already executing.");for(;a&&(a=0,l[0]&&(i=0)),i;)try{if(r=1,n&&(o=2&l[0]?n.return:l[0]?n.throw||((o=n.return)&&o.call(n),0):n.next)&&!(o=o.call(n,l[1])).done)return o;switch(n=0,o&&(l=[2&l[0],o.value]),l[0]){case 0:case 1:o=l;break;case 4:return i.label++,{value:l[1],done:!1};case 5:i.label++,n=l[1],l=[0];continue;case 7:l=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==l[0]&&2!==l[0])){i=0;continue}if(3===l[0]&&(!o||l[1]>o[0]&&l[1]0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},u=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockBuilderPromise=void 0;var f=r(2603),c=u(r(5551)),d=r(6456),s=u(r(2970)),v=r(5974),p=r(3659),h=u(r(8073)),y=r(3771),_=r(9539),g=u(r(7140)),b=u(r(1649)),m=u(r(4387)),M=u(r(1267)),k=u(r(9961)),w=u(r(7703)),O=u(r(5195)),x=u(r(1391)),j=u(r(7235)),D=u(r(8792)),S=u(r(9102)),P=function(e){return(0,p.isNgModuleDefWithProviders)(e)?{def:e.ngModule,providers:e.providers}:{def:e,providers:void 0}},C=function(e,t,r){return r?i(i([],a(Array.isArray(t)?t:[]),!1),[e],!1):e},N={},T=function(){function e(e){this.configDefault=e,this.beforeCC=new Set,this.configDef=new Map,this.defProviders=new Map,this.defValue=new Map,this.excludeDef=new Set,this.keepDef=new Set,this.mockDef=new Set,this.providerDef=new Map,this.replaceDef=new Set,this.stash=new _.MockBuilderStash,"undefined"!=typeof Symbol&&(this[Symbol.toStringTag]="Promise")}return e.prototype.beforeCompileComponents=function(e){return this.beforeCC.add(e),this},e.prototype.build=function(){this.stash.backup();var e=new c.default;h.default.config.set("mockNgDefResolver",e),h.default.flags.add("hasRootModule");try{var t=this.combineParams(),r=(0,x.default)(t,(0,j.default)(t));return(0,g.default)(r,t,e),(0,O.default)(r,t,e),(0,w.default)(r),(0,b.default)(),r.providers.push((0,M.default)(),(0,k.default)(),(0,m.default)(this.replaceDef,this.defValue),y.MockBuilder),r}finally{h.default.flags.delete("hasRootModule"),h.default.config.delete("mockNgDefResolver"),this.stash.restore()}},e.prototype.catch=function(e){return n(this,void 0,Promise,(function(){return o(this,(function(t){return[2,this.then().catch(e)]}))}))},e.prototype.exclude=function(e){return this.wipe(e),this.excludeDef.add(e),this.setConfigDef(e),this},e.prototype.finally=function(e){return n(this,void 0,Promise,(function(){return o(this,(function(t){return[2,this.then().finally(e)]}))}))},e.prototype.keep=function(e,t){var r=P(e),n=r.def,o=r.providers,l=this.keepDef.has(n)?this.defProviders.get(n):[];return this.wipe(n),this.keepDef.add(n),o&&this.defProviders.set(n,i(i([],a(l||[]),!1),a(o),!1)),this.setConfigDef(n,t),this},e.prototype.mock=function(e,t,r){void 0===t&&(t=N);var n=P(e),o=n.def,l=n.providers,u=(0,D.default)(o,t,r,N),f=u.config,c=u.mock;if((0,v.isNgDef)(c)&&(0,v.isNgDef)(e)&&!(0,v.isNgDef)(e,"t"))throw new Error(["MockBuilder.mock(".concat((0,s.default)(e),") received a class when its shape is expected."),"Please try ngMocks.defaultMock instead."].join(" "));var d=this.mockDef.has(o)?this.defProviders.get(o):[];return this.wipe(o),this.mockDef.add(o),l&&this.defProviders.set(o,i(i([],a(d||[]),!1),a(l),!1)),this.setDefValue(o,c),this.setConfigDef(o,f),this},e.prototype.provide=function(e){var t,r;try{for(var n=l((0,d.flatten)(e)),o=n.next();!o.done;o=n.next()){var a=o.value,i=(0,S.default)(a),u=i.provide,f=i.multi,c=this.providerDef.has(u)?this.providerDef.get(u):[];this.providerDef.set(u,C(a,c,f))}}catch(e){t={error:e}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(t)throw t.error}}return this},e.prototype.replace=function(e,t,r){if(!(0,v.isNgDef)(t)||!(0,v.isNgDef)(e)||(0,v.isNgDef)(t,"i")||(0,v.isNgDef)(e,"i"))throw new Error("Cannot replace the declaration, both have to be a Module, a Component, a Directive or a Pipe, for Providers use `.mock` or `.provide`");return this.wipe(e),this.replaceDef.add(e),this.defValue.set(e,t),this.setConfigDef(e,r),this},e.prototype.then=function(e,t){return n(this,void 0,Promise,(function(){var r,n=this;return o(this,(function(o){return r=new Promise((function(e){var t,r,o=f.TestBed.configureTestingModule(n.build());try{for(var a=l((0,d.mapValues)(n.beforeCC)),i=a.next();!i.done;i=a.next())(0,i.value)(o)}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}o.compileComponents().then((function(){e({testBed:o})}))})),[2,r.then(e,t)]}))}))},e.prototype.combineParams=function(){return{configDef:this.configDef,configDefault:this.configDefault,defProviders:this.defProviders,defValue:this.defValue,excludeDef:this.excludeDef,keepDef:this.keepDef,mockDef:this.mockDef,providerDef:this.providerDef,replaceDef:this.replaceDef}},e.prototype.setConfigDef=function(e,t){!t&&this.configDef.has(e)||this.configDef.set(e,null!=t?t:this.configDefault)},e.prototype.setDefValue=function(e,t){t===N?this.defValue.delete(e):this.defValue.set(e,t)},e.prototype.wipe=function(e){this.defProviders.delete(e),this.defValue.delete(e),this.excludeDef.delete(e),this.keepDef.delete(e),this.mockDef.delete(e),this.providerDef.delete(e),this.replaceDef.delete(e)},e}();t.MockBuilderPromise=T},3771:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockBuilder=s;var i=a(r(3174)),l=r(6456),u=r(6580),f=a(r(8073)),c=a(r(5020)),d=r(9755);function s(){for(var e,t,r,a,s,v,p=[],h=0;h=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0});var o=r(6456);t.default=function(e,t,r){var a,i;if(void 0===r&&(r=function(e,t){return e===t}),!t||t.size!==e.size)return!1;try{for(var l=n((0,o.mapKeys)(e)),u=l.next();!u.done;u=l.next()){var f=u.value;if(!t.has(f))return!1;if(!r(t.get(f),e.get(f)))return!1}}catch(e){a={error:e}}finally{try{u&&!u.done&&(i=l.return)&&i.call(l)}finally{if(a)throw a.error}}return!0}},7570:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=function(e,t){for(var r,o,a=[],i=2;i=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0});var o=r(6456);t.default=function(e,t){var r,a;if(!t||t.size!==e.size)return!1;try{for(var i=n((0,o.mapValues)(e)),l=i.next();!l.done;l=i.next()){var u=l.value;if(!t.has(u))return!1}}catch(e){r={error:e}}finally{try{l&&!l.done&&(a=i.return)&&a.call(i)}finally{if(r)throw r.error}}return!0}},2801:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(5432));t.default=function(e,t){return!(!(0,a.default)(t,e)||"object"==typeof t&&"object"==typeof e&&function(e,t){var r,o;if(Object.keys(e).length!==Object.keys(t).length)return!0;try{for(var i=n(Object.keys(e)),l=i.next();!l.done;l=i.next()){var u=l.value;if(!(0,a.default)(e[u],t[u]))return!0}}catch(e){r={error:e}}finally{try{l&&!l.done&&(o=i.return)&&o.call(i)}finally{if(r)throw r.error}}return!1}(t,e))}},5432:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(9382));t.default=function(e,t){return e===t||("boolean"!=typeof e&&"boolean"!=typeof t||e===t)&&e.$implicit===t.$implicit&&(0,o.default)(e.variables,t.variables)}},9382:function(e,t){var r=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){return e===t||!(e&&!t||!e&&t)&&!function(e,t){var n,o,a=Object.keys(e),i=Object.keys(t);if(a.length!==i.length)return!0;try{for(var l=r(a),u=l.next();!u.done;u=l.next()){var f=u.value;if(e[f]!==t[f])return!0}}catch(e){n={error:e}}finally{try{u&&!u.done&&(o=l.return)&&o.call(l)}finally{if(n)throw n.error}}return!1}(e,t)}},5633:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{beforeCC:new Set,configDef:new Map,defProviders:new Map,defValue:new Map,excludeDef:new Set,keepDef:new Set,mockDef:new Set,providerDef:new Map,replaceDef:new Set}}},4503:function(e,t){var r=this&&this.__assign||function(){return r=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8911)),l=o(r(6297)),u=o(r(8073)),f=o(r(3663));t.default=function(e,t,r){var o,c,d,s,v,p,h=t.providerDef,y=t.mockDef;try{for(var _=n((0,a.mapValues)(h)),g=_.next();!g.done;g=_.next()){var b=g.value;e.providers.push(b)}}catch(e){o={error:e}}finally{try{g&&!g.done&&(c=_.return)&&c.call(_)}finally{if(o)throw o.error}}try{for(var m=n((0,a.flatten)(e.providers)),M=m.next();!M.done;M=m.next()){b=M.value;var k=(0,l.default)(b);u.default.touches.add(k),k!==b&&b.deps&&(0,a.extractDependency)(b.deps,u.default.config.get("ngMocksDeps"))}}catch(e){d={error:e}}finally{try{M&&!M.done&&(s=m.return)&&s.call(m)}finally{if(d)throw d.error}}try{for(var w=n((0,a.mapValues)(y)),O=w.next();!O.done;O=w.next()){var x=O.value;u.default.touches.has(x)||"root"!==(0,i.default)(x)||(e.providers.push((0,f.default)(x,r)),u.default.touches.add(x))}}catch(e){v={error:e}}finally{try{O&&!O.done&&(p=w.return)&&p.call(w)}finally{if(v)throw v.error}}}},1649:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(2603),i=r(6456),l=o(r(6297)),u=o(r(8073));t.default=function(){var e,t,r=(0,a.getTestBed)();if(r.ngModule)try{for(var o=n((0,i.flatten)(r.ngModule)),f=o.next();!f.done;f=o.next()){var c=f.value;u.default.touches.add((0,l.default)(c))}}catch(t){e={error:t}}finally{try{f&&!f.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}}},646:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t,r){"function"==typeof e&&-1===r.indexOf(e)&&(r.push(e),t.push(e))}},4387:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(1381)),l=r(1763),u=o(r(8073)),f=o(r(5884)),c=o(r(4483));t.default=function(e,t){var r,o,d,s,v=new Map;try{for(var p=n((0,a.mapValues)(u.default.touches)),h=p.next();!h.done;h=p.next()){var y=h.value,_=u.default.getBuildDeclaration(y)||y;if(!(0,c.default)(e,t,y,_)){var g=(0,i.default)(_),b=(0,f.default)(g);if(b){var m={};try{for(var M=(d=void 0,n(Object.keys(b))),k=M.next();!k.done;k=M.next()){var w=k.value;m[w]=g[w]}}catch(e){d={error:e}}finally{try{k&&!k.done&&(s=M.return)&&s.call(M)}finally{if(d)throw d.error}}v.set(_,[{set:b},{set:m}])}}}}catch(e){r={error:e}}finally{try{h&&!h.done&&(o=p.return)&&o.call(p)}finally{if(r)throw r.error}}return{provide:l.NG_MOCKS_OVERRIDES,useValue:v}}},1267:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=r(6456),u=r(1763),f=i(r(8073));t.default=function(){var e,t,r=new Map;try{for(var i=a(o(o(o(o([],n((0,l.mapEntries)(f.default.builtProviders)),!1),n((0,l.mapEntries)(f.default.builtDeclarations)),!1),n((0,l.mapEntries)(f.default.cacheDeclarations)),!1),n((0,l.mapEntries)(f.default.cacheProviders)),!1)),c=i.next();!c.done;c=i.next()){var d=n(c.value,2),s=d[0],v=d[1];r.has(s)||r.set(s,v)}}catch(t){e={error:t}}finally{try{c&&!c.done&&(t=i.return)&&t.call(i)}finally{if(e)throw e.error}}return{provide:u.NG_MOCKS,useValue:r}}},9961:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=r(1763),l=o(r(8073));t.default=function(){var e,t,r=new Set;try{for(var o=n((0,a.mapValues)(l.default.touches)),u=o.next();!u.done;u=o.next()){var f=u.value,c=l.default.getBuildDeclaration(f);void 0===c&&(c=f),r.add(f),r.add(c)}}catch(t){e={error:t}}finally{try{u&&!u.done&&(t=o.return)&&t.call(o)}finally{if(e)throw e.error}}return{provide:i.NG_MOCKS_TOUCHES,useValue:r}}},8608:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(7285));t.default=function(e){var t,r;if(e){var o;try{for(var i=n(e),l=i.next();!l.done;l=i.next()){var u=l.value;u&&"object"==typeof u&&u.token&&(o=u.token),o||!u||"object"==typeof u&&u.ngMetadataName||(o=u)}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}return(0,a.default)(o)}}},5884:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(8073)),i=o(r(224));t.default=function(e){if(e){var t=a.default.flags.has("skipMock");t||a.default.flags.add("skipMock");var r=n((0,i.default)(e),2),o=r[0],l=r[1];if(t||a.default.flags.delete("skipMock"),o)return l}}},8703:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(8749)),i=r(1763),l=o(r(8073)),u=o(r(2504)),f=o(r(646)),c=o(r(8608)),d=o(r(1978)),s=o(r(7213)),v=o(r(4995));t.default=function(e){var t,r,o,p,h,y,_=new Set,g=(0,d.default)(),b=g.buckets,m=g.touched;try{for(var M=n(b),k=M.next();!k.done;k=M.next()){var w=k.value;try{for(var O=(o=void 0,n(w)),x=O.next();!x.done;x=O.next()){var j=x.value;(0,u.default)(_,e,j);try{for(var D=(h=void 0,n((0,a.default)(j))),S=D.next();!S.done;S=D.next()){var P=S.value,C=(0,c.default)(P);(0,s.default)(C),(0,v.default)(C)||((0,f.default)(C,m,w),e.has(i.NG_MOCKS_ROOT_PROVIDERS)||!l.default.config.get("ngMocksDepsSkip").has(j)?_.add(C):l.default.config.get("ngMocksDepsSkip").add(C))}}catch(e){h={error:e}}finally{try{S&&!S.done&&(y=D.return)&&y.call(D)}finally{if(h)throw h.error}}}}catch(e){o={error:e}}finally{try{x&&!x.done&&(p=O.return)&&p.call(O)}finally{if(o)throw o.error}}}}catch(e){t={error:e}}finally{try{k&&!k.done&&(r=M.return)&&r.call(M)}finally{if(t)throw t.error}}return _}},1978:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=r(860),u=i(r(3174)),f=r(6456),c=r(1763),d=r(5974),s=i(r(1365)),v=function(e,t){if(t){var r=t.resolveComponentFactory;t.resolveComponentFactory=(0,s.default)(r,void 0,void 0,(function(a){for(var i,l=[],u=1;u=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=r(1763),l=r(4152),u=o(r(8073)),f=o(r(3663)),c=o(r(9465)),d=o(r(8703));t.default=function(e,t,r){var o,s,v=t.keepDef,p=t.mockDef,h=v.has(i.NG_MOCKS_ROOT_PROVIDERS)?new Set:(0,d.default)(p);if(h.size>0){var y=function(t){var n=(0,f.default)(t,r);if(n)e.providers.push(n);else if((0,l.isNgInjectionToken)(t)){var o=u.default.config.has("ngMocksMulti")&&u.default.config.get("ngMocksMulti").has(t);e.providers.push((0,c.default)(t,(function(){return o?[]:void 0})))}};try{for(var _=n((0,a.mapValues)(h)),g=_.next();!g.done;g=_.next())y(g.value)}catch(e){o={error:e}}finally{try{g&&!g.done&&(s=_.return)&&s.call(_)}finally{if(o)throw o.error}}}}},2710:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8073));t.default=function(e){var t,r,o=i.default.builtDeclarations,l=i.default.builtProviders,u=i.default.config.get("ngMocksDepsResolution");try{for(var f=n((0,a.mapValues)(e)),c=f.next();!c.done;c=f.next()){var d=c.value;o.set(d,null),l.set(d,null),u.set(d,"exclude")}}catch(e){t={error:e}}finally{try{c&&!c.done&&(r=f.return)&&r.call(f)}finally{if(t)throw t.error}}}},7651:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=r(5204),l=o(r(8073));t.default=function(e,t){var r,o,u=new Set,f=l.default.builtDeclarations,c=l.default.builtProviders,d=l.default.config.get("ngMocksDepsResolution");try{for(var s=n((0,a.mapValues)(e)),v=s.next();!v.done;v=s.next()){var p=v.value;f.set(p,p),c.set(p,p),d.set(p,"keep"),t.get(p).shallow&&(0,i.funcExtractDeps)(p,u)}}catch(e){r={error:e}}finally{try{v&&!v.done&&(o=s.return)&&o.call(s)}finally{if(r)throw r.error}}return u}},102:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8073)),l=o(r(4161));t.default=function(e,t){var r,o,u=i.default.builtDeclarations,f=i.default.config.get("ngMocksDepsResolution");try{for(var c=n((0,a.mapValues)(e)),d=c.next();!d.done;d=c.next()){var s=d.value,v=!i.default.touches.has(s);f.set(s,"mock"),u.set(s,void 0),(0,l.default)(s,t),v&&i.default.touches.delete(s)}}catch(e){r={error:e}}finally{try{d&&!d.done&&(o=c.return)&&o.call(c)}finally{if(r)throw r.error}}}},3680:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8073));t.default=function(e,t){var r,n=null!==(r=o.default.config.get("mockNgDefResolver").get(e))&&void 0!==r?r:o.default.getBuildDeclaration(e),a=t.has(e)?t.get(e):void 0;return a?{ngModule:n,providers:a}:n}},1159:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=r(6456),u=r(5974),f=i(r(8073)),c=r(3821),d=i(r(224)),s=i(r(4673)),v=i(r(8150));t.default=function(e,t,r,i){var p,h,y,_,g,b,m,M,k,w=new Map;try{for(var O=a(o(o(o([],n((0,l.mapValues)(e)),!1),n((0,l.mapValues)(t)),!1),n((0,l.mapValues)(r)),!1)),x=O.next();!x.done;x=O.next()){var j=x.value,D=(0,s.default)(j),S=o(o(o([],n(null!==(g=i.get(j))&&void 0!==g?g:[]),!1),n(null!==(m=null===(b=D.Component)||void 0===b?void 0:b.providers)&&void 0!==m?m:[]),!1),n(null!==(k=null===(M=D.Directive)||void 0===M?void 0:M.providers)&&void 0!==k?k:[]),!1),P=!f.default.touches.has(j);t.has(j)||f.default.flags.add("skipMock");var C=(0,u.isNgDef)(j,"m");if(S.length>0){var N=n((0,d.default)({providers:S,skipMarkProviders:!C,skipExports:!0}),2)[1];w.set(j,N.providers)}C&&f.default.builtDeclarations.set(j,(0,c.MockModule)(j)),f.default.flags.delete("skipMock"),P&&f.default.touches.delete(j)}}catch(e){p={error:e}}finally{try{x&&!x.done&&(h=O.return)&&h.call(O)}finally{if(p)throw p.error}}try{for(var T=a((0,l.mapValues)(t)),E=T.next();!E.done;E=T.next())j=E.value,(0,v.default)(j)}catch(e){y={error:e}}finally{try{E&&!E.done&&(_=T.return)&&_.call(T)}finally{if(y)throw y.error}}return w}},1391:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8073));t.default=function(e,t){var r,o,l=i.default.builtDeclarations,u=i.default.config.get("ngMocksDepsResolution");try{for(var f=n((0,a.mapValues)(e)),c=f.next();!c.done;c=f.next()){var d=c.value;l.set(d,t.get(d)),u.set(d,"replace")}}catch(e){r={error:e}}finally{try{c&&!c.done&&(o=f.return)&&o.call(f)}finally{if(r)throw r.error}}}},7235:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=r(6456),u=r(5204),f=i(r(8073)),c=i(r(2710)),d=i(r(7651)),s=i(r(102)),v=i(r(1159)),p=i(r(8500));t.default=function(e){var t,r,i,h,y,_,g,b,m,M,k,w,O=e.configDef,x=e.defProviders,j=e.defValue,D=e.excludeDef,S=e.keepDef,P=e.mockDef,C=e.replaceDef;f.default.flags.add("cachePipe"),f.default.config.set("ngMocksMulti",new Set),f.default.config.set("ngMocksDeps",new Set),f.default.config.set("ngMocksDepsSkip",new Set),f.default.config.set("ngMocksDepsResolution",new Map);var N=(0,d.default)(S,O);try{for(var T=o((0,l.mapValues)(N)),E=T.next();!E.done;E=T.next()){var A=E.value;f.default.touches.add(A)}}catch(e){t={error:e}}finally{try{E&&!E.done&&(r=T.return)&&r.call(T)}finally{if(t)throw t.error}}try{for(var R=o((0,l.mapValues)(S)),I=R.next();!I.done;I=R.next())A=I.value,N.add(A),(0,u.funcExtractDeps)(A,N,!0)}catch(e){i={error:e}}finally{try{I&&!I.done&&(h=R.return)&&h.call(R)}finally{if(i)throw i.error}}try{for(var V=o((0,l.mapValues)(P)),B=V.next();!B.done;B=V.next())A=B.value,N.add(A),(0,u.funcExtractDeps)(A,N,!0)}catch(e){y={error:e}}finally{try{B&&!B.done&&(_=V.return)&&_.call(V)}finally{if(y)throw y.error}}try{for(var F=o((0,l.mapValues)(C)),L=F.next();!L.done;L=F.next())A=L.value,N.add(A),(0,u.funcExtractDeps)(A,N,!0)}catch(e){g={error:e}}finally{try{L&&!L.done&&(b=F.return)&&b.call(F)}finally{if(g)throw g.error}}try{for(var K=o((0,l.mapValues)(N)),G=K.next();!G.done;G=K.next())if(A=G.value,!O.has(A)){var q=f.default.getResolution(A);"replace"===q?(C.add(A),j.set(A,f.default.getBuildDeclaration(A))):"keep"===q?S.add(A):"exclude"===q?D.add(A):("mock"===q||f.default.touches.has(A))&&P.add(A),O.set(A,f.default.touches.has(A)?{dependency:!0,__internal:!0}:{})}}catch(e){m={error:e}}finally{try{G&&!G.done&&(M=K.return)&&M.call(K)}finally{if(m)throw m.error}}try{for(var U=o((0,l.mapEntries)(O)),z=U.next();!z.done;z=U.next()){var W=a(z.value,2),H=W[0],$=W[1];f.default.config.set(H,n(n(n({},f.default.getConfigMock().get(H)),$),{defValue:j.get(H)}))}}catch(e){k={error:e}}finally{try{z&&!z.done&&(w=U.return)&&w.call(U)}finally{if(k)throw k.error}}return(0,p.default)(C,j),(0,c.default)(D),(0,s.default)(P,j),(0,v.default)(S,P,C,x)}},8792:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(5974);t.default=function(e,t,r,o){var a=e===t?o:t,i=null!=r?r:t!==o&&"object"==typeof t?t:void 0;return(0,n.isNgDef)(e,"p")&&"function"==typeof t&&t!==e&&!(0,n.isNgDef)(t,"p")?(a={transform:t},i=r):!(0,n.isNgDef)(e,"i")&&(0,n.isNgDef)(e)||(i=r),{config:i,mock:a=a===i?o:a}}},9102:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(6297));t.default=function(e){var t=(0,o.default)(e);return{multi:t!==e&&e.multi,provide:t}}},70:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(4358),a=n(r(3295)),i=n(r(8911)),l=r(4152),u=n(r(8073));t.default=function(e){if(function(e){if(!e||e===o.DOCUMENT||u.default.touches.has(e))return!0;var t=function(e){var t=u.default.getResolution(e);return"keep"===t||"exclude"===t||"mock"!==t&&void 0}(e);return void 0!==t?t:"function"==typeof e&&-1!==a.default.neverMockProvidedFunction.indexOf(e.name)||!(!(0,l.isNgInjectionToken)(e)||-1===a.default.neverMockToken.indexOf(e.toString()))}(e))return!0;var t=(0,i.default)(e);return!("function"!=typeof e||t&&"platform"!==t)}},4483:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8073));t.default=function(e,t,r,n){return!!o.default.cacheDeclarations.has(n)||!(!e.has(r)||n!==t.get(r))}},4995:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8073)),a=n(r(70));t.default=function(e){return!!(0,a.default)(e)||o.default.config.get("ngMocksDepsSkip").has(e)}},8150:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(5974),a=n(r(8073)),i=r(5395),l=r(8339),u=r(2937);t.default=function(e){void 0===a.default.builtDeclarations.get(e)&&((0,o.isNgDef)(e,"c")&&a.default.builtDeclarations.set(e,(0,i.MockComponent)(e)),(0,o.isNgDef)(e,"d")&&a.default.builtDeclarations.set(e,(0,l.MockDirective)(e)),(0,o.isNgDef)(e,"p")&&a.default.builtDeclarations.set(e,(0,u.MockPipe)(e)))}},4161:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(5974),a=n(r(8073)),i=n(r(8195)),l=n(r(9465)),u=n(r(2415));t.default=function(e,t){if((0,o.isNgDef)(e,"i")&&t.has(e)){var r=a.default.config.get(e),n=t.get(e);a.default.builtProviders.set(e,(0,l.default)(e,void 0,(function(e){return function(e,t,r){return r.precise?t:(0,i.default)(e,t)}(e,n,r)})))}else(0,o.isNgDef)(e,"i")&&a.default.builtProviders.set(e,(0,u.default)(e,!0));if(!(0,o.isNgDef)(e)&&t.has(e)){var f=t.get(e);a.default.builtProviders.set(e,(0,l.default)(e,void 0,(function(){return f})))}else(0,o.isNgDef)(e)||a.default.builtProviders.set(e,(0,u.default)(e,!0))}},2938:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},5395:function(e,t,r){var n,o=this&&this.__extends||(n=function(e,t){return n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r])},n(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function r(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)}),a=this&&this.__assign||function(){return a=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},l=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},u=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockComponents=function(){for(var e=[],t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0});var o=r(860),a={read:o.ViewContainerRef,static:!1},i={read:o.TemplateRef,static:!1},l=function(e,t){var r="
    ");return"").concat(r,"")},u=function(e){return!(e.isViewQuery||e.read&&e.read!==o.TemplateRef||"string"!=typeof e.selector&&!e.read)};t.default=function(e){var t,r,f=[""];if(!e)return f.join("");try{for(var c=n(Object.keys(e)),d=c.next();!d.done;d=c.next()){var s=d.value,v=e[s];if(0!==s.indexOf("__mock")&&u(v)){if("string"==typeof v.selector){var p=v.selector.replace(new RegExp("\\W","mg"),"_");e["__vcrIf_key_".concat(p)]=new o.ViewChild("ngIf_key_".concat(p),a),e["__trIf_key_".concat(p)]=new o.ViewChild("ngIf_key_".concat(p),i),e["__mockView_key_".concat(p)]=new o.ViewChild("key_".concat(p),a),e["__mockTpl_key_".concat(p)]=v,f.push(l(p,"key"))}e["__vcrIf_prop_".concat(s)]=new o.ViewChild("ngIf_prop_".concat(s),a),e["__trIf_prop_".concat(s)]=new o.ViewChild("ngIf_prop_".concat(s),i),e["__mockView_prop_".concat(s)]=new o.ViewChild("prop_".concat(s),a),f.push(l(s,"prop"))}}}catch(e){t={error:e}}finally{try{d&&!d.done&&(r=c.return)&&r.call(c)}finally{if(t)throw t.error}}return f.join("")}},8970:function(e,t){var r=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i};Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){if("string"==typeof e)return["key","__mockTpl_key_".concat(e),e,void 0];var t=r(e),n=t[0],o=t.slice(1);return["prop",n,n,o.length>0?o:void 0]}},2350:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},5269:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockDeclarations=function(){for(var e=[],t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(1184)),l=a(r(6755)),u=a(r(1692));t.default=function(e,t){return function(r){return!!function(e,t,r){for(var n,a,i,l=(null===(n=e.injector._tNode)||void 0===n?void 0:n.attrs)||[],u=2,f=0;f0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(5465)),i=o(r(908));t.default=function(e){return function(t){var r=n((0,i.default)(t),2),o=r[0];return-1!==r[1].indexOf(e)||!!(0,a.default)(o,e)}}},12:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4201)),a=r(6739);t.default=function(e){var t=(0,a.getSourceOfMock)(e);return function(e){return!!e&&-1!==e.providerTokens.indexOf(t)&&void 0!==(0,o.default)(t,e.injector)}}},1863:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return function(t){return!!t.references[e]}}},5494:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(908)),i=o(r(4747));t.default=function(e){return function(t){var r=n((0,a.default)(t),1)[0];return(0,i.default)(r,e)}}},5465:function(e,t){var r=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){var n,o,a,i;try{for(var l=r(e),u=l.next();!u.done;u=l.next()){var f=u.value.match(/\[([^=\]]+)/g);if(f)try{for(var c=(a=void 0,r(f)),d=c.next();!d.done;d=c.next())if(d.value==="[".concat(t))return!0}catch(e){a={error:e}}finally{try{d&&!d.done&&(i=c.return)&&i.call(c)}finally{if(a)throw a.error}}}}catch(e){n={error:e}}finally{try{u&&!u.done&&(o=l.return)&&o.call(l)}finally{if(n)throw n.error}}return!1}},4476:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8864)),a=n(r(8060)),i=n(r(12)),l=n(r(1863)),u=n(r(5494));t.default=function(e){if(function(e){return Array.isArray(e)&&1===e.length&&"string"==typeof e[0]}(e))return(0,o.default)(e[0]);if(function(e){return Array.isArray(e)&&2===e.length&&"string"==typeof e[0]}(e))return(0,a.default)(e[0],e[1]);if(function(e){return"string"==typeof e&&0===e.indexOf("#")&&e.length>1}(e))return(0,l.default)(e.slice(1));if(function(e){return"string"==typeof e&&0!==e.indexOf("#")&&e.length>0}(e))return(0,u.default)(e);if(function(e){return"function"==typeof e}(e))return(0,i.default)(e);throw new Error("Unknown selector")}},908:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0});var n=new RegExp("\\[.*?\\]","g");t.default=function(e,t){var o,a,i,l;try{for(var u=r(e),f=u.next();!f.done;f=u.next()){var c=f.value.replace(n,"").split(",");try{for(var d=(i=void 0,r(c)),s=d.next();!s.done;s=d.next())if(s.value.trim()===t)return!0}catch(e){i={error:e}}finally{try{s&&!s.done&&(l=d.return)&&l.call(d)}finally{if(i)throw i.error}}}}catch(e){o={error:e}}finally{try{f&&!f.done&&(a=u.return)&&a.call(u)}finally{if(o)throw o.error}}return!1}},3942:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return"#text"===e.nativeNode.nodeName}},5079:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){return!(!e||!t)&&e===t}},6121:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(3942));t.default=function(e){return(0,o.default)(e)?void 0:e.injector._tNode||e.injector.elDef||void 0}},1061:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(860),l=a(r(4201)),u=a(r(6121)),f=function(e,t){var r=function(e,t){if(e!==t&&"#comment"===t.nativeNode.nodeName)return(0,l.default)(i.ViewContainerRef,t.injector)}(e,t);if(!r)return[];for(var n=[],o=0;o0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(590)),i=o(r(5687)),l=o(r(951)),u=o(r(4476)),f=o(r(3942)),c=o(r(6547)),d=o(r(8344));t.default=function(){for(var e=[],t=0;t0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(590)),i=o(r(5687)),l=o(r(951)),u=o(r(4811)),f=o(r(4476)),c=o(r(3942)),d=o(r(6547)),s=o(r(8344)),v={};t.default=function(){for(var e=[],t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3942)),i=o(r(5079)),l=o(r(6121)),u=o(r(1061));t.default=function(e){var t,r,o,f,c=(0,l.default)(e);if(!c||(0,a.default)(e))return[];var d=void 0!==e.childNodes,s=[];try{for(var v=n(e.childNodes||(null===(o=e.parent)||void 0===o?void 0:o.childNodes)||[]),p=v.next();!p.done;p=v.next()){var h=p.value,y=(0,u.default)(h);(d||(0,i.default)(c,y))&&(y&&!(0,i.default)(c,y)||s.push(h))}}catch(e){t={error:e}}finally{try{p&&!p.done&&(r=v.return)&&r.call(v)}finally{if(t)throw t.error}}if("BODY"===(null===(f=e.parent)||void 0===f?void 0:f.name)){for(var _=e.parent.childNodes,g=_.length,b=0,m=_.length-1;m>=0;m-=1)if("#comment"===(h=_[m]).nativeNode.nodeName)b=m;else if(h.nativeNode===e.nativeNode){g=m+1;break}for(m=g;m=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(5079)),i=o(r(6121)),l=o(r(1061));t.default=function(e,t){var r,o,u;if(t)return t;var f=(0,l.default)(e),c=e.parent?(0,i.default)(e.parent):void 0;if(e.parent&&(0,a.default)(f,c))return e.parent;try{for(var d=n((null===(u=e.parent)||void 0===u?void 0:u.childNodes)||[]),s=d.next();!s.done;s=d.next()){var v=s.value,p=(0,i.default)(v);if((0,a.default)(f,p))return v}}catch(e){r={error:e}}finally{try{s&&!s.done&&(o=d.return)&&o.call(d)}finally{if(r)throw r.error}}}},8732:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3942)),i=o(r(7790)),l=o(r(345)),u=function(e,t,r,o){var f,c;if(void 0===o&&(o=!1),!e)return!1;if(!o&&(0,a.default)(e))return!1;if(r(e,(0,l.default)(e,t)))return!0;try{for(var d=n((0,i.default)(e)),s=d.next();!s.done;s=d.next()){var v=s.value;if(u(v,e,r,o))return!0}}catch(e){f={error:e}}finally{try{s&&!s.done&&(c=d.return)&&c.call(d)}finally{if(f)throw f.error}}return!1};t.default=u},6016:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(2023)),a=n(r(4201)),i=["Cannot find ControlValueAccessor on the element.","If it is a mock input with [formControlName],","you need either to avoid mocking ReactiveFormsModule","or to avoid accessing the control in such a way,","because this tests ReactiveFormsModule instead of own implementation."].join(" ");t.default=function(e){var t=o.default&&(0,a.default)(o.default.NgControl,e.injector),r=null==t?void 0:t.valueAccessor;if(r)return r;var n=o.default&&(0,a.default)(o.default.FormControlDirective,e.injector);if(null==n?void 0:n.form)return n.form;var l=o.default&&(0,a.default)(o.default.NgModel,e.injector);if(l)return l;throw new Error(i)}},2298:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(2023)),i=r(7105),l=o(r(5006)),u=o(r(7331)),f=o(r(1991)),c=o(r(590)),d=o(r(5687)),s=o(r(4811)),v=o(r(589)),p=o(r(6016)),h=["onChange","onChangeCallback","onChangeCb","onChangeClb","onChangeFn","_onChange","_onChangeCallback","_onChangeCb","_onChangeClb","_onChangeFn","changeFn","_changeFn","onModelChange","cvaOnChange","cvaOnChangeCallback","cvaOnChangeCb","cvaOnChangeClb","cvaOnChangeFn","_cvaOnChange","_cvaOnChangeCallback","_cvaOnChangeCb","_cvaOnChangeClb","_cvaOnChangeFn"];t.default=function(e,t,r){var o,y,_=(0,c.default)((0,d.default)(),e,void 0);if(!_)throw new Error("Cannot find an element via ngMocks.change(".concat((0,s.default)(e),")"));var g=(0,p.default)(_);if(!function(e,t){return a.default&&e instanceof a.default.AbstractControl?(e.setValue(t),!0):a.default&&e instanceof a.default.NgModel?(e.update.emit(t),!0):!!(0,i.isMockControlValueAccessor)(e.instance)&&(e.instance.__simulateChange(t),!0)}(g,t)&&!function(e){return e.listeners.some((function(e){return"input"===e.name||"change"===e.name}))}(_)){try{for(var b=n(r?[r]:h),m=b.next();!m.done;m=b.next()){var M=m.value;if("function"==typeof g[M])return g.writeValue(t),void g[M](t)}}catch(e){o={error:e}}finally{try{m&&!m.done&&(y=b.return)&&y.call(b)}finally{if(o)throw o.error}}var k=(0,u.default)(g);throw new Error(["Unsupported type of ControlValueAccessor,","please ensure it has '".concat(r||"onChange","' method."),"If it is a 3rd-party library, please provide the correct name of the method in the 'methodName' parameter.","Possible Names: "+k.join(", ")+"."].join(" "))}!function(e,t){(0,f.default)(e,"focus");var r=Object.getOwnPropertyDescriptor(e.nativeElement,"value");(0,v.default)(e.nativeElement,"value",t),(0,f.default)(e,"input"),(0,f.default)(e,"change"),r&&((0,l.default)(e.nativeElement,"value",r),e.nativeElement.value=t),(0,f.default)(e,"blur")}(_,t)}},7655:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(2023)),i=r(7105),l=o(r(7331)),u=o(r(1991)),f=o(r(590)),c=o(r(5687)),d=o(r(4811)),s=o(r(6016)),v=["onTouched","onTouchedCallback","onTouchedCb","onTouchedClb","onTouchedFn","_onTouched","_onTouchedCallback","_onTouchedCb","_onTouchedClb","_onTouchedFn","markAsTouched","_markAsTouched","onModelTouched","cvaOnTouch","cvaOnTouchCallback","cvaOnTouchCb","cvaOnTouchClb","cvaOnTouchFn","_cvaOnTouch","_cvaOnTouchCallback","_cvaOnTouchCb","_cvaOnTouchClb","_cvaOnTouchFn"];t.default=function(e,t){var r,o,p=(0,f.default)((0,c.default)(),e,void 0);if(!p)throw new Error("Cannot find an element via ngMocks.touch(".concat((0,d.default)(e),")"));var h=(0,s.default)(p);if(!function(e){return a.default&&e instanceof a.default.AbstractControl?(e.markAsTouched(),!0):!!(0,i.isMockControlValueAccessor)(e.instance)&&(e.instance.__simulateTouch(),!0)}(h)&&!function(e){return e.listeners.some((function(e){return"focus"===e.name||"blur"===e.name}))}(p)){try{for(var y=n(t?[t]:v),_=y.next();!_.done;_=y.next()){var g=_.value;if("function"==typeof h[g])return void h[g]()}}catch(e){r={error:e}}finally{try{_&&!_.done&&(o=y.return)&&o.call(y)}finally{if(r)throw r.error}}var b=(0,l.default)(h);throw new Error(["Unsupported type of ControlValueAccessor,","please ensure it has '".concat(t||"onTouched","' method."),"If it is a 3rd-party library, please provide the correct name of the method in the 'methodName' parameter.","Possible Names: "+b.join(", ")+"."].join(" "))}!function(e){(0,u.default)(e,"focus"),(0,u.default)(e,"blur")}(p)}},9903:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(1991));t.default=function(e,t){(0,o.default)(e,"click",t)}},6843:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});for(var l=i(r(8195)),u=["focus","blur","load","unload","change","reset","scroll"],f="function"==typeof Event?function(e,t){return new CustomEvent(e,t)}:function(e,t){var r=n({bubbles:!1,cancelable:!1},t),o=document.createEvent("CustomEvent");return o.initCustomEvent(e,r.bubbles,r.cancelable,null),o},c={alt:{altKey:!0,code:"AltLeft",key:"Alt",location:1,which:18},arrowdown:{code:"ArrowDown",key:"ArrowDown",location:0,which:40},arrowleft:{code:"ArrowLeft",key:"ArrowLeft",location:0,which:37},arrowright:{code:"ArrowRight",key:"ArrowRight",location:0,which:39},arrowup:{code:"ArrowUp",key:"ArrowUp",location:0,which:38},backspace:{code:"Backspace",key:"Backspace",location:0,which:8},control:{code:"ControlLeft",ctrlKey:!0,key:"Control",location:1,which:17},enter:{code:"Enter",key:"Enter",location:0,which:13},esc:{code:"Escape",key:"Escape",location:0,which:27},meta:{code:"MetaLeft",key:"Meta",location:1,metaKey:!0,which:91},shift:{code:"ShiftLeft",key:"Shift",location:1,shiftKey:!0,which:16},space:{code:"Space",key:" ",location:0,which:32},tab:{code:"Tab",key:"Tab",location:0,which:9}},d=1;d<=12;d+=1)c["f".concat(d)]={code:"F".concat(d),key:"F".concat(d),location:0,which:d+111};t.default=function(e,t,r){var i=e.indexOf("."),d=a(-1===i?[e]:[e.slice(0,Math.max(0,i)),e.slice(i+1)],2),s=d[0],v=d[1],p=f(s,n({bubbles:-1===u.indexOf(e),cancelable:!0},t));return function(e,t){var r,n,a,i,u={};try{for(var f=o(t?t.split("."):[]),d=f.next();!d.done;d=f.next()){var s=d.value,v=c[s];if(v||1!==s.length||(v={code:(a=s,void 0,i=a.codePointAt(0),i&&i>=97&&i<=122||i&&i>=65&&i<=90?"Key".concat(a.toUpperCase()):i&&i>=48&&i<=57?"Digit".concat(a):"Unknown"),key:s}),!v)throw new Error("Unknown event part ".concat(s));(0,l.default)(u,v)}}catch(e){r={error:e}}finally{try{d&&!d.done&&(n=f.return)&&n.call(f)}finally{if(r)throw r.error}}t&&(0,l.default)(e,u)}(p,v),r&&(0,l.default)(p,r),p}},1991:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(590)),a=n(r(771)),i=n(r(4792)),l=n(r(4993)),u=n(r(5687)),f=n(r(8195)),c=n(r(6843)),d=["focus","blur","load","unload","change","reset","scroll"];t.default=function(e,t,r){var n,s=(n=(0,l.default)(e)?e:(0,o.default)((0,u.default)(),e,void 0),(0,a.default)(n)||(0,i.default)(n)?n.nativeElement:(0,l.default)(n)?n:void 0);if(!s)throw new Error("Cannot trigger ".concat("string"==typeof t?t:t.type," event undefined element"));if(!s.disabled){var v=function(e){return"string"==typeof e?(0,c.default)(e,{bubbles:-1===d.indexOf(e),cancelable:!0}):e}(t);v.target||(0,f.default)(v,{target:s}),r&&(0,f.default)(v,r),s.dispatchEvent(v)}}},343:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(5974);t.default=function(e){return"function"==typeof e||(0,n.isNgDef)(e,"t")}},4212:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=r(6739),l=r(5974),u=o(r(8344)),f=o(r(590)),c=o(r(1771)),d=o(r(5687)),s=o(r(951)),v=o(r(4811)),p=o(r(343)),h={};t.default=function(){for(var e=[],t=0;t0}),!0);else try{b.push((0,a.getInjection)(g))}catch(e){if(!e||"object"!=typeof e||void 0===e.ngTempTokenPath)throw e}if(b.length>0)return b[0];if(_!==h)return _;throw new Error("Cannot find an instance via ngMocks.findInstance(".concat((0,v.default)(y),")"))}},147:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(6456),l=r(6739),u=r(5974),f=a(r(8344)),c=a(r(7316)),d=a(r(1771)),s=a(r(5687)),v=a(r(951)),p=a(r(343));t.default=function(){for(var e,t,r=[],a=0;a0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(4792)),i=o(r(5687)),l=function(e){return(0,a.default)(e)?l(e.debugElement):e&&e.injector&&e.query?e:void 0};t.default=function(e,t){var r,o,a,u,f=t;return 3===e.length?(a=l(e[0]),u=e[1],f=e[2]):1===e.length?(a=l((0,i.default)()),u=n(e,1)[0]):e[0]?(a=l(e[0]))?u=e[1]:(a=l((0,i.default)()),u=(r=n(e,2))[0],f=r[1]):u=e[1],[a,u=null!==(o=l(u))&&void 0!==o?o:u,f]}},654:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(1165),o=r(6739);t.default=function(e){return Array.isArray(e)?n.By.css(1===e.length?"[".concat(e[0],"]"):"[".concat(e[0],'="').concat(e[1],'"]')):"string"==typeof e?n.By.css(e):n.By.directive((0,o.getSourceOfMock)(e))}},7316:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(771)),i=o(r(7453)),l=o(r(654));t.default=function(){for(var e=[],t=0;t0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(771)),i=o(r(4811)),l=o(r(7453)),u=o(r(654)),f={};t.default=function(){for(var e=[],t=0;t\\s+<","mg"),"><").replace(new RegExp('"\\s+>',"mg"),'">'):"";return r?n:n.trim()}var o;return(0,i.default)(t)?e(function(e,t){return t?e.outerHTML:e.innerHTML}(t,r)):(0,l.default)(t)?u(e,(0,a.default)(t).replace(new RegExp("&","mg"),"&").replace(new RegExp('"',"mg"),""").replace(new RegExp("<","mg"),"<").replace(new RegExp(">","mg"),">").replace(new RegExp("'","mg"),"'"),r):void 0};t.default=(0,o.default)(u)},9512:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4858)),a=n(r(9280)),i=n(r(4993)),l=n(r(8209)),u=function(e,t,r){if("string"==typeof t||void 0===t){var n=(o=t)?o.replace(new RegExp("\\s+","mg")," "):"";return r?n:n.trim()}var o;return(0,i.default)(t)?e(function(e,t){var r,n=null!==(r=e.textContent)&&void 0!==r?r:"";return t?n:n.trim()}(t,r)):(0,l.default)(t)?u(e,(0,a.default)(t),r):void 0};t.default=(0,o.default)(u)},3341:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t;return"#text"===(null===(t=e.nativeNode)||void 0===t?void 0:t.nodeName)&&e.parent?e.parent:e}},7717:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4201)),a=r(5974);t.default=function(e,t,r){if(t.injector&&"NullInjector"!==t.injector.constructor.name){var n=function(e){for(var t=e;"NullInjector"===(null==t?void 0:t.injector.constructor.name);)t=t.parent;if(t)return t.injector}(t.parent),i=n?(0,o.default)(r,n):void 0,l=(0,o.default)(r,t.injector);i!==l&&((0,a.isNgDef)(r,"t")&&void 0!==l||void 0!==l&&-1===e.indexOf(l))&&e.push(l)}}},2631:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(3341)),a=n(r(1484)),i=function(e){for(var t,r=e,n=null===(t=r.nativeNode)||void 0===t?void 0:t.__ngContext__;void 0===n&&r.parent;)n=(r=r.parent).nativeNode.__ngContext__;if("number"!=typeof n)return n;var o=r.injector._lView;return Array.isArray(o)?function(e,t){if("object"==typeof e[1]&&e[20]===t)return e;for(var r=21;r1&&f[1]&&"object"==typeof f[1]&&f[1].bindingStartIndex&&(v=f[1].bindingStartIndex);for(var p=0;p=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3341)),i=o(r(1484)),l=function(e){var t,r;if(!e||"object"!=typeof e)return e;try{for(var o=n(["renderElement","renderText","instance"]),a=o.next();!a.done;a=o.next()){var i=a.value;if(e[i])return e[i]}}catch(e){t={error:e}}finally{try{a&&!a.done&&(r=o.return)&&r.call(o)}finally{if(t)throw t.error}}return null};t.default=function(e,t,r){if(t&&t._debugContext){var n=(0,a.default)(t);(0,i.default)({el:n,nodes:t._debugContext.view.nodes,normalize:l,proto:r,result:e},!0)}}},1771:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(5974),a=n(r(7717)),i=n(r(2631)),l=n(r(2096));t.default=function(e,t,r){return(0,a.default)(e,t,r),(0,o.isNgDef)(r,"t")||"string"==typeof r||((0,l.default)(e,t,r),(0,i.default)(e,t,r)),e}},5687:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(2603);t.default=function(){var e=(0,n.getTestBed)()._activeFixtures;return e[e.length-1]}},4317:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(8073));t.default=function(){var e;o.default.cacheDeclarations.clear(),null===(e=o.default.config.get("ngMocksDepsSkip"))||void 0===e||e.clear()}},4811:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(5974);t.default=function(e){return"string"==typeof e?e:"function"==typeof e?e.name:(0,n.isNgDef)(e,"t")?e._desc:Array.isArray(e)?e[0]:e?"":""}},951:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(771)),i=o(r(4792)),l=o(r(5687));t.default=function(e,t,r){var o,u,f,c,d,s,v=r;return 3===e.length?(c=(o=n(e,3))[0],d=o[1],v=o[2]):1===e.length?(c=(0,l.default)(),d=n(e,1)[0]):t(e[1])&&("string"==typeof(s=e[0])||Array.isArray(s)&&"string"==typeof s[0]||(0,i.default)(s)||(0,a.default)(s),1)?(c=(u=n(e,2))[0],d=u[1]):(c=(0,l.default)(),d=(f=n(e,2))[0],v=f[1]),[c,d,v]}},4641:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(4201)),a=n(r(8862)),i=n(r(6297));t.default=function(e,t){if(e)try{var r=(0,i.default)(t);return function(e){try{return(0,a.default)(e)}catch(e){return}}((0,o.default)(r,e.injector).constructor)}catch(e){return}}},5018:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(1184)),l=a(r(590)),u=a(r(5687)),f=a(r(4641)),c=a(r(8027)),d={},s=function(e,t){var r=(0,i.default)(e),n=r.name,o=r.alias,a=void 0===o?"":o;if(!a&&n===t||a&&a===t)return n};t.default=function(e,t){for(var r=[],a=2;a=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3174));t.default=function(e,t){return function(){for(var r=[],o=0;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8073));t.default=function(e,t){var r,o,l=i.default.getConfigMock();try{for(var u=n((0,a.flatten)(e)),f=u.next();!f.done;f=u.next()){var c=f.value;t?l.set(c,t):l.delete(c)}}catch(e){r={error:e}}finally{try{f&&!f.done&&(o=u.return)&&o.call(u)}finally{if(r)throw r.error}}}},8909:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(8073));t.default=function(e,t){var r,o,l=i.default.getOverrides();try{for(var u=n((0,a.flatten)(e)),f=u.next();!f.done;f=u.next()){var c=f.value;if(t){var d=l.has(c)?l.get(c):new Set;d.add(t),l.set(c,d)}else l.delete(c)}}catch(e){r={error:e}}finally{try{f&&!f.done&&(o=u.return)&&o.call(u)}finally{if(r)throw r.error}}}},1640:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(2603),i=o(r(3174)),l=o(r(8073)),u=l.default.global.get("faster-hooks")||{after:[],before:[]};l.default.global.set("faster-hooks",u);var f=function(e,t){return function(r){var o,f;if(a.TestBed.ngMocksFasterLock)return e.call(t,r);l.default.global.set("bullet:customized",!0);var c=e;try{for(var d=n(u.before),s=d.next();!s.done;s=d.next())c=(0,s.value)(c,t)}catch(e){o={error:e}}finally{try{s&&!s.done&&(f=d.return)&&f.call(d)}finally{if(o)throw o.error}}try{return(0,i.default)(a.TestBed,"ngMocksFasterLock",!0),c.call(t,r)}finally{(0,i.default)(a.TestBed,"ngMocksFasterLock",void 0)}}},c=function(e,t){return function(){var r,o;if(a.TestBed.ngMocksFasterLock)return e.call(t);if(l.default.global.has("bullet"))return l.default.global.has("bullet:customized")&&l.default.global.set("bullet:reset",!0),t;l.default.global.delete("bullet:customized"),l.default.global.delete("bullet:reset");var f=e;try{for(var c=n(u.after),d=c.next();!d.done;d=c.next())f=(0,d.value)(f,t)}catch(e){r={error:e}}finally{try{d&&!d.done&&(o=c.return)&&o.call(c)}finally{if(r)throw r.error}}try{return(0,i.default)(a.TestBed,"ngMocksFasterLock",!0),f.call(t)}finally{(0,i.default)(a.TestBed,"ngMocksFasterLock",void 0)}}};t.default=function(){a.TestBed.ngMocksFasterInstalled||(a.TestBed.configureTestingModule=f(a.TestBed.configureTestingModule,a.TestBed),a.TestBed.resetTestingModule=c(a.TestBed.resetTestingModule,a.TestBed),(0,i.default)(a.TestBed,"ngMocksFasterInstalled",!0));var e=(0,a.getTestBed)();return e.ngMocksFasterInstalled||(e.configureTestingModule=f(e.configureTestingModule,e),e.resetTestingModule=c(e.resetTestingModule,e),(0,i.default)(e,"ngMocksFasterInstalled",!0)),u}},7346:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(2603),a=n(r(8073)),i=n(r(1640)),l=n(r(2776)),u=function(e){var t,r=null!==(t=a.default.global.get("bullet:stack"))&&void 0!==t?t:[];r.push(e),a.default.global.set("bullet:stack",r),a.default.global.set("bullet:stack:id",e)},f=function(e){var t=a.default.global.get("bullet:stack");t.splice(t.indexOf(e),1),t.length>0?a.default.global.set("bullet:stack:id",t[t.length-1]):a.default.global.delete("bullet:stack:id"),function(e){for(var t=(0,o.getTestBed)()._activeFixtures||[],r=0,n=t.length-1;n>=0;n-=1)t[n].ngMocksStackId&&t[n].ngMocksStackId!==e?r+=1:(t[n].ngMocksStackId=void 0,t[n].destroy(),t.splice(n,1));0===r&&(0,l.default)()}(e)};t.default=function(){(0,i.default)();var e={},t={};beforeAll((function(){a.default.global.has("bullet:customized")&&o.TestBed.resetTestingModule(),a.default.global.set("bullet",!0),u(e)})),beforeEach((function(){u(t)})),afterEach((function(){f(t)})),afterAll((function(){f(e),a.default.global.delete("bullet"),a.default.global.has("bullet:reset")&&o.TestBed.resetTestingModule()}))}},2776:function(e,t,r){Object.defineProperty(t,"__esModule",{value:!0});var n=r(2603);t.default=function(){var e=(0,n.getTestBed)();e._instantiated=!1,e._moduleFactory=void 0,e._testModuleRef=null}},8027:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(2603),a=n(r(2970)),i=r(6739),l=n(r(345)),u=n(r(590)),f=n(r(1771)),c=n(r(5687)),d=n(r(4811)),s={};t.default=function(){for(var e=[],t=0;t0)return _[0];if(h){var _,g=(0,l.default)(h,void 0);if(g&&"#comment"===g.nativeNode.nodeName&&(_=(0,f.default)([],g,y)).length>0)return _[0]}if(p!==s)return p;throw new Error("Cannot find ".concat((0,a.default)(v)," instance via ngMocks.get"))}},4071:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(6259)),a=n(r(8073)),i=n(r(4317)),l=function(e){a.default.getDefaults().set(e,["exclude"])};t.default=function(e,t){void 0===t&&(t=!1),(0,i.default)(),l(e),t&&(0,o.default)(e,l)}},5228:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(6259)),a=n(r(8073)),i=n(r(4317)),l=function(e){a.default.getDefaults().set(e,["keep"])};t.default=function(e,t){void 0===t&&(t=!1),(0,i.default)(),l(e),t&&(0,o.default)(e,l)}},763:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(6259)),a=n(r(8073)),i=n(r(4317)),l=function(e){a.default.getDefaults().set(e,["mock"])};t.default=function(e,t){void 0===t&&(t=!1),(0,i.default)(),l(e),t&&(0,o.default)(e,l)}},3757:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(5974),a=n(r(8073)),i=n(r(4317));t.default=function(e,t){var r=!0;if(((0,o.isNgDef)(e,"m")&&(0,o.isNgDef)(t,"m")||(0,o.isNgDef)(e,"c")&&(0,o.isNgDef)(t,"c")||(0,o.isNgDef)(e,"d")&&(0,o.isNgDef)(t,"d")||(0,o.isNgDef)(e,"p")&&(0,o.isNgDef)(t,"p"))&&(r=!1),r)throw new Error("Cannot replace the declaration, both have to be a Module, a Component, a Directive or a Pipe");(0,i.default)(),a.default.getDefaults().set(e,["replace",t])}},4102:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(6259)),a=n(r(8073)),i=n(r(4317)),l=n(r(8909)),u=function(e){a.default.getDefaults().delete(e),(0,l.default)(e)};t.default=function(e,t){void 0===t&&(t=!1),(0,i.default)(),u(e),t&&(0,o.default)(e,u)}},7418:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(5551)),l=r(6456),u=a(r(1345)),f=a(r(6297)),c=r(5974),d=r(4152),s=r(3659),v=a(r(8073)),p=r(5395),h=r(8339),y=r(3821),_=r(2937),g=a(r(2415)),b=function(e,t,r){return!!t.has(e)||(t.add(e),r.has(e))},m=function(e,t,r,n,o){var a=e.get(t)||t;(0,c.isNgDef)(a,"m")?r.push(a):(0,c.isNgDef)(a,"c")||(0,c.isNgDef)(a,"d")?n.push(a):(0,c.isNgDef)(a,"p")?(n.push(a),o.push(a)):(0,d.isNgInjectionToken)(a)||o.push(a)},M=[["m","module"],["c","component"],["d","directive"],["p","pipe"]],k=function(e,t,r,n){b(t,e.skip,e.exclude)||n.push(e.keep.has(t)?t:r(t))},w=function(e,t){var r=e.skip,n=e.keep,o=e.providers,a=e.exclude,i=(0,f.default)(t);if(r.add(i),!a.has(i)){var l=n.has(i)?t:(0,g.default)(t);l&&o.push(l)}},O={component:p.MockComponent,directive:h.MockDirective,pipe:_.MockPipe},x=function(e,t,r){if(void 0===r&&(r=!0),t){var a,i=function(e,t){var r,a;if((0,s.isNgModuleDefWithProviders)(e))return"module-with-providers";try{for(var i=n(M),l=i.next();!l.done;l=i.next()){var u=o(l.value,2),f=u[0],d=u[1];if((0,c.isNgDef)(e,f))return"m"===f&&t.has(e)?"".concat(d,"-keep"):d}}catch(e){r={error:e}}finally{try{l&&!l.done&&(a=i.return)&&a.call(i)}finally{if(r)throw r.error}}return""}(t,e.keep);if("module-with-providers"!==i){var f=e.optional.get(t);f&&f!==t&&(a=f,e.keep.add(a))}a||(a=t),function(e,t,r,o){"module-with-providers"===t?function(e,t){e.skip.has(t.ngModule)||(e.skip.add(t.ngModule),e.exclude.has(t.ngModule)||e.imports.push(e.keep.has(t.ngModule)?t:(0,y.MockModule)(t)))}(e,r):"module-keep"===t||"module"===t&&o?k(e,r,y.MockModule,e.imports):"module"===t?function(e,t,r){var o,a,i,f;if(!b(t,e.skip,e.exclude)){var c=(0,u.default)(t);try{for(var d=n((0,l.flatten)([c.declarations,c.imports])),s=d.next();!s.done;s=d.next())r(e,h=s.value)}catch(e){o={error:e}}finally{try{s&&!s.done&&(a=d.return)&&a.call(d)}finally{if(o)throw o.error}}try{for(var v=n(c.providers?(0,l.flatten)(c.providers):[]),p=v.next();!p.done;p=v.next()){var h=p.value;w(e,h)}}catch(e){i={error:e}}finally{try{p&&!p.done&&(f=v.return)&&f.call(v)}finally{if(i)throw i.error}}}}(e,r,x):O[t]?k(e,r,O[t],e.declarations):w(e,r)}(e,i,a,r)}};t.default=function(e,t,r){var o,a,u,f,c,d;void 0===t&&(t=null),void 0===r&&(r=null);var s=function(e,t,r){var o=new Set((0,l.flatten)(e||[])),a=new Set((0,l.flatten)(t||[])),i=new Set((0,l.flatten)(r||[])),u=new Map;return function(e,t,r,o){var a,i;try{for(var u=n((0,l.mapKeys)(v.default.getDefaults())),f=u.next();!f.done;f=u.next()){var c=f.value,d=v.default.getBuildDeclaration(c);e.has(c)||t.has(c)||r.has(c)||(o.set(c,d),null===d?r.add(c):void 0===d?t.add(c):c===d&&e.add(c))}}catch(e){a={error:e}}finally{try{f&&!f.done&&(i=u.return)&&i.call(u)}finally{if(a)throw a.error}}}(o,a,i,u),{declarations:[],exclude:i,imports:[],keep:o,mock:a,optional:u,providers:[],skip:new Set}}(e,t,r),p=new Map;v.default.config.set("ngMocksDepsResolution",p);try{for(var h=n((0,l.mapValues)(s.keep)),y=h.next();!y.done;y=h.next()){var _=y.value;p.set(_,"keep")}}catch(e){o={error:e}}finally{try{y&&!y.done&&(a=h.return)&&a.call(h)}finally{if(o)throw o.error}}try{for(var g=n((0,l.mapValues)(s.exclude)),b=g.next();!b.done;b=g.next())_=b.value,p.set(_,"exclude")}catch(e){u={error:e}}finally{try{b&&!b.done&&(f=g.return)&&f.call(g)}finally{if(u)throw u.error}}v.default.config.set("mockNgDefResolver",new i.default);try{for(var M=n((0,l.mapValues)(s.mock)),k=M.next();!k.done;k=M.next()){var w=k.value;p.set(w,"mock"),s.optional.has(w)||x(s,w,!1)}}catch(e){c={error:e}}finally{try{k&&!k.done&&(d=M.return)&&d.call(M)}finally{if(c)throw c.error}}var O=function(e){var t,r,o=e.keep,a=e.skip,i=e.optional,l=e.exclude,u=e.imports,f=e.declarations,c=e.providers;try{for(var d=n(o),s=d.next();!s.done;s=d.next()){var v=s.value;a.has(v)||l.has(v)||i.has(v)||m(i,v,u,f,c)}}catch(e){t={error:e}}finally{try{s&&!s.done&&(r=d.return)&&r.call(d)}finally{if(t)throw t.error}}return{declarations:f,imports:u,providers:c}}(s);return v.default.config.delete("mockNgDefResolver"),v.default.config.delete("ngMocksDepsResolution"),O}},7411:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3295)),i=o(r(8073)),l=o(r(8344)),u=o(r(8458)),f=o(r(9400)),c=o(r(2298)),d=o(r(7655)),s=o(r(9903)),v=o(r(6843)),p=o(r(1991)),h=o(r(590)),y=o(r(7316)),_=o(r(4212)),g=o(r(147)),b=o(r(8062)),m=o(r(9512)),M=o(r(3539)),k=o(r(7977)),w=o(r(9311)),O=o(r(2237)),x=o(r(8909)),j=o(r(7346)),D=o(r(2776)),S=o(r(8027)),P=o(r(4071)),C=o(r(5228)),N=o(r(763)),T=o(r(3757)),E=o(r(4102)),A=o(r(7418)),R=o(r(7411)),I=o(r(5526)),V=o(r(2448)),B=o(r(8195)),F=o(r(589)),L=o(r(3048)),K=o(r(2120)),G=o(r(1368)),q=o(r(5535)),U=["onMockBuilderMissingDependency","onMockInstanceRestoreNeed","onTestBedFlushNeed"];t.default={autoSpy:M.default,change:c.default,click:s.default,config:function(e){var t,r,o=i.default.global.get("flags");try{for(var l=n(U),u=l.next();!u.done;u=l.next()){var f=u.value;null===e[f]?o[f]=a.default[f]:void 0!==e[f]&&(o[f]=e[f])}}catch(e){t={error:e}}finally{try{u&&!u.done&&(r=l.return)&&r.call(l)}finally{if(t)throw t.error}}null===e.mockRenderCacheSize?i.default.global.delete("mockRenderCacheSize"):void 0!==e.mockRenderCacheSize&&i.default.global.set("mockRenderCacheSize",e.mockRenderCacheSize)},crawl:l.default,defaultConfig:O.default,defaultMock:x.default,event:v.default,faster:j.default,find:h.default,findAll:y.default,findInstance:_.default,findInstances:g.default,findTemplateRef:G.default,findTemplateRefs:q.default,flushTestBed:D.default,formatHtml:b.default,formatText:m.default,get:S.default,globalExclude:P.default,globalKeep:C.default,globalMock:N.default,globalReplace:T.default,globalWipe:E.default,guts:A.default,hide:L.default,ignoreOnConsole:k.default,input:R.default,output:I.default,render:K.default,reset:V.default,reveal:u.default,revealAll:f.default,stub:B.default,stubMember:F.default,throwOnConsole:w.default,touch:d.default,trigger:p.default}},5526:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=i(r(7331)),u=i(r(7794)),f=i(r(5020)),c=i(r(445));t.default=function(e,t,r){var i,d;if("string"==typeof t)return c.default.mock(e,t,r);var s=e,v=t,p=["__zone_symbol__unconfigurables"];"function"==typeof t&&(s=c.default.createClone(t),v=e,p.push.apply(p,o([],n(Object.getOwnPropertyNames(s)),!1)));var h=o(o([],n((0,l.default)(v)),!1),n((0,u.default)(v)),!1);try{for(var y=a(h),_=y.next();!_.done;_=y.next()){var g=_.value,b=-1===p.indexOf(g)?(0,f.default)(v,g):void 0;b&&Object.prototype.hasOwnProperty.call(b,"value")&&void 0===b.value||c.default.definePropertyDescriptor(s,g,b)}}catch(e){i={error:e}}finally{try{_&&!_.done&&(d=y.return)&&d.call(y)}finally{if(i)throw i.error}}return s}},6189:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.ngMocks=void 0;var o=n(r(492));t.ngMocks=o.default},845:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(860),l=a(r(2137)),u=function(e,t,r){return!!e&&t instanceof i.TemplateRef&&r(t)},f=function(e,t,r){var a,c;if(!(0,l.default)(e))throw new Error("Only instances of mock declarations are accepted");if(function(e,t,r){return!!e.__template&&!!e.__vcr&&t(e.__template)&&r(e.__vcr,e.__template)}(e,t,r))return!0;try{for(var d=n(function(e){var t,r,o=[];try{for(var a=n(e.__ngMocksConfig.queryScanKeys||[]),l=a.next();!l.done;l=a.next())for(var u=l.value,f=e[u],c=e["__ngMocksVcr_".concat(u)],d=f instanceof i.QueryList?f.toArray():[f],s=c instanceof i.QueryList?c.toArray():[c],v=0;v0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(8732)),i=o(r(590)),l=o(r(5687)),u=o(r(951)),f=o(r(4811)),c=o(r(2789)),d=o(r(6703)),s=o(r(7845)),v={};t.default=function(){for(var e=[],t=0;t0)return y[0];if(h!==v)return h;throw new Error("Cannot find a TemplateRef via ngMocks.findTemplateRef(".concat((0,f.default)(p),")"))}},5535:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(8732)),i=o(r(590)),l=o(r(5687)),u=o(r(951)),f=o(r(2789)),c=o(r(6703)),d=o(r(7845));t.default=function(){for(var e=[],t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(8073)),l=a(r(589));t.default=function(e){var t,r,a=[],u=i.default.configInstance.get(e);if(null==u?void 0:u.overloads){var f=function(e,t,r){e?a.push((function(n){(0,l.default)(n,e,t,r)})):a.push(t)};try{for(var c=n(u.overloads),d=c.next();!d.done;d=c.next()){var s=o(d.value,3);f(s[0],s[1],s[2])}}catch(e){t={error:e}}finally{try{d&&!d.done&&(r=c.return)&&r.call(c)}finally{if(t)throw t.error}}}return a}},6691:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(2970)),i=o(r(8073));t.default=function(e){for(var t=[];e.length>0;){var r=n(e.pop()||[],2),o=r[0];r[1]===i.default.configInstance.get(o)&&t.push("function"==typeof o?(0,a.default)(o):o)}if(t.length>0){var l=i.default.global.get("flags"),u=["MockInstance: side effects have been detected (".concat(t.join(", "),")."),"Forgot to add MockInstance.scope() or to call MockInstance.restore()?"].join(" ");if("warn"===l.onMockInstanceRestoreNeed)console.warn(u);else if("throw"===l.onMockInstanceRestoreNeed)throw new Error(u)}}},3001:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockInstance=h,t.MockReset=function(){f.default.configInstance.clear()};var i,l=a(r(6804)),u=a(r(7592)),f=a(r(8073)),c=a(r(6691));u.default.subscribePush((function(e){i=e})),u.default.subscribePop((function(e,t){var r,a;try{for(var l=o(e.mockInstance||[]),u=l.next();!u.done;u=l.next()){var c=u.value;if(f.default.configInstance.has(c)){var d=f.default.configInstance.get(c);d.overloads.pop(),f.default.configInstance.set(c,n({},d))}}}catch(e){r={error:e}}finally{try{u&&!u.done&&(a=l.return)&&a.call(l)}finally{if(r)throw r.error}}i=t[t.length-1]}));var d=function(e){var t={};return"string"==typeof e[0]?(t.key=e[0],t.value=e[1],t.accessor=e[2]):(t.value=e[0],t.value&&"object"==typeof t.value&&(t.value=t.value.init)),t},s=[],v=!1;"undefined"!=typeof beforeEach&&(beforeEach((function(){return v=!0})),beforeEach((function(){return(0,c.default)(s)})),afterEach((function(){return v=!1})));var p=function(e,t,r,o){var a,l=f.default.configInstance.has(e)?f.default.configInstance.get(e):{},u=l.overloads||[];u.push([t,r,o]),l.overloads=u,f.default.configInstance.set(e,n({},l));var c=null!==(a=i.mockInstance)&&void 0!==a?a:[];return c.push(e),i.mockInstance=c,v&&s.push([e,f.default.configInstance.get(e),i]),r};function h(e){for(var t=[],r=1;r0){var o=d(t),a=o.key,u=o.value,c=o.accessor;return p(e,a,u,c)}var v=f.default.configInstance.get(e)||{};f.default.configInstance.set(e,n(n({},v),{overloads:[]}));for(var h=s.length-1;h>=0;h-=1)s[h][0]===e&&s[h][2]===i&&s.splice(h,1)}!function(e){e.remember=function(){u.default.stackPush()},e.restore=function(){u.default.stackPop()},e.scope=function(t){void 0===t&&(t="case"),"all"!==t&&"suite"!==t||(beforeAll(e.remember),afterAll(e.restore)),"all"!==t&&"case"!==t||(beforeEach(e.remember),afterEach(e.restore))}}(h||(t.MockInstance=h={}))},9988:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(6297)),l=r(5974),u=r(3659),f=a(r(8073)),c=r(5395),d=r(8339),s=r(2937),v=a(r(445)),p=r(3821),h=[["c",c.MockComponent],["d",d.MockDirective],["p",s.MockPipe]];t.default=function(e,t){var r=function(e,t){return function(r){var a;if(e.has(r))return function(e,t,r){var n=t.get(e);return e!==n&&r(),n}(r,e,t);var c=(0,i.default)(r);if(f.default.isExcludedDef(c))return function(e,t,r){t.set(e,void 0),r()}(r,e,t);f.default.touches.add(c);var d=function(e){var t,r;if((0,l.isNgDef)(e,"m")||(0,u.isNgModuleDefWithProviders)(e))return(0,p.MockModule)(e);if(f.default.hasBuildDeclaration(e))return f.default.getBuildDeclaration(e);if(f.default.flags.has("skipMock")&&"mock"!==f.default.getResolution(e))return e;try{for(var a=n(h),i=a.next();!i.done;i=a.next()){var c=o(i.value,2),d=c[0],s=c[1];if((0,l.isNgDef)(e,d))return s(e)}}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}}(r);return function(e,t){return(0,u.isNgModuleDefWithProviders)(t)&&(0,u.isNgModuleDefWithProviders)(e)}(r,d)&&e.set(r.ngModule,d.ngModule),f.default.flags.has("skipMock")&&(null===(a=f.default.config.get("ngMocksDepsSkip"))||void 0===a||a.add(d)),e.set(r,d),t(d!==r),d}}(t,e),a=function(e,t){return function(r){return v.default.resolveProvider(r,e,t)}}(t,e);return{resolve:r,resolveProvider:a}}},6860:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=r(6456),i=o(r(6297)),l=o(r(7610));t.default=function(e){var t,r;try{for(var o=n((0,a.flatten)(null!=e?e:[])),u=o.next();!u.done;u=o.next()){var f=u.value,c=(0,i.default)(f);(0,l.default)(c)}}catch(e){t={error:e}}finally{try{u&&!u.done&&(r=o.return)&&r.call(o)}finally{if(t)throw t.error}}}},3821:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockModule=function(e){var t;(0,v.default)(e,"MockModule");var r=w(e),n=r.ngModule,o=r.ngModuleProviders,a=M(n);try{var i=x(n,O(n,a.isRootModule));g.default.flags.has("cacheModule")&&g.default.cacheDeclarations.set(n,i),g.default.flags.has("skipMock")&&(null===(t=g.default.config.get("ngMocksDepsSkip"))||void 0===t||t.add(i));var l=j(o);return D(e,n,o,i,l)}finally{k(a)}};var i=r(860),l=a(r(3295)),u=a(r(3174)),f=r(6456),c=a(r(1345)),d=a(r(615)),s=a(r(2970)),v=a(r(6804)),p=r(6763),h=r(5974),y=r(3659),_=r(1946),g=a(r(8073)),b=a(r(5937)),m=a(r(224)),M=function(e){var t=!1,r=!0;g.default.flags.has("hasRootModule")?r=!1:g.default.flags.add("hasRootModule");var n=g.default.getResolution(e);return function(e){return"mock"===e&&g.default.flags.has("skipMock")}(n)&&(t=!0,g.default.flags.delete("skipMock")),function(e){return-1!==l.default.neverMockModule.indexOf((0,s.default)(e))&&!g.default.flags.has("skipMock")}(e)&&(t=!0,g.default.flags.add("skipMock")),r||!function(e){return"keep"===e&&!g.default.flags.has("skipMock")}(n)&&!function(e){return"replace"===e&&!g.default.flags.has("skipMock")}(n)||(t=!0,g.default.flags.add("skipMock")),{isRootModule:r,toggleSkipMockFlag:t}},k=function(e){var t=e.isRootModule,r=e.toggleSkipMockFlag;r&&g.default.flags.has("skipMock")?g.default.flags.delete("skipMock"):r&&!g.default.flags.has("skipMock")&&g.default.flags.add("skipMock"),t&&g.default.flags.delete("hasRootModule")},w=function(e){var t,r;return(0,y.isNgModuleDefWithProviders)(e)?(t=e.ngModule,e.providers&&(r=e.providers)):t=e,{ngModule:t,ngModuleProviders:r}},O=function(e,t){var r;if((0,p.isMockNgDef)(e,"m"))return e;if(g.default.flags.has("cacheModule")&&g.default.cacheDeclarations.has(e))return(0,b.default)(e);if(!t&&"mock"!==(null===(r=g.default.config.get("ngMocksDepsResolution"))||void 0===r?void 0:r.get(e))&&g.default.hasBuildDeclaration(e)){var n=g.default.getBuildDeclaration(e);if((0,h.isNgDef)(n,"m")&&n!==e)return n}},x=function(e,t){var r=o(t?[!1]:(0,m.default)((0,c.default)(e),e),3),n=r[0],a=r[1],l=r[2];if(l&&(0,u.default)(e,"__ngMocksResolutions",l),n){var s=g.default.flags.has("skipMock")?e:_.Mock,v=(0,f.extendClass)(s);return(0,i.NgModule)(a)(v),(0,d.default)(v,e),v}return t||e},j=function(e){if(e){var t=o((0,m.default)({providers:e,skipExports:!0}),2),r=t[0],n=t[1];return r?n.providers:e}},D=function(e,t,r,o,a){return o===t&&a===r?e:(0,y.isNgModuleDefWithProviders)(e)?n({ngModule:o},a?{providers:a}:{}):o}},224:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=i(r(5551)),u=r(6456),f=i(r(6297)),c=i(r(8073)),d=i(r(7610)),s=i(r(9988)),v=i(r(6860)),p=function(e,t){return(0,u.flatten)(e).map(t).filter((function(e){return!!e}))},h=function(e,t,r,o){var a=c.default.config.get(o)||{},i=(0,f.default)(e),l=t(i);if(l){var u=c.default.config.get(i);if((null==u?void 0:u.export)&&o&&!a.export&&c.default.config.set(o,n(n({},a),{export:!0})),!r||a.exportAll||(null==u?void 0:u.export))return(0,d.default)(i,o),l}},y=function(e,t){return!e||!!t.exports&&-1!==t.exports.indexOf(e)};t.default=function(e,t){var r,i,_=c.default.config.has("mockNgDefResolver");_||c.default.config.set("mockNgDefResolver",new l.default),c.default.config.get("mockNgDefResolver").push();var g=!c.default.flags.has("skipMock"),b=function(e){void 0===e&&(e=!0),g=g||e},m=(0,s.default)(b,c.default.config.get("mockNgDefResolver")),M=m.resolve,k=function(e,t,r){var i,l,u,d={},s=function(e,t){return[["declarations",e],["hostDirectives",function(t){var r=(0,f.default)(t),o=e(r);return o===r?t:t==r?o:n(n({},t),{directive:o})}],["imports",e],["entryComponents",e],["bootstrap",e],["providers",t],["viewProviders",t],["exports",e],["schemas",function(e){return e}]]}(t,r),h=c.default.flags.has("cachePipe");h||c.default.flags.add("cachePipe");try{for(var y=o(s),_=y.next();!_.done;_=y.next()){var g=a(_.value,2),b=g[0],m=g[1];(null===(u=e[b])||void 0===u?void 0:u.length)&&(d[b]=p(e[b],m))}}catch(e){i={error:e}}finally{try{_&&!_.done&&(l=y.return)&&l.call(y)}finally{if(i)throw i.error}}return e.skipMarkProviders||((0,v.default)(d.providers),(0,v.default)(d.viewProviders)),h||c.default.flags.delete("cachePipe"),d}(e,M,m.resolveProvider);e.skipExports||function(e,t,r,n,a){var i,l,f=c.default.flags.has("skipMock")||c.default.flags.has("correctModuleExports");try{for(var d=o((0,u.flatten)([r.imports||[],r.declarations||[]])),s=d.next();!s.done;s=d.next()){var v=s.value,p=h(v,e,f,a);y(p,n)||(t(),n.exports=n.exports||[],n.exports.push(p))}}catch(e){i={error:e}}finally{try{s&&!s.done&&(l=d.return)&&l.call(d)}finally{if(i)throw i.error}}}(M,b,e,k,t);try{for(var w=o(t&&k.exports?(0,u.flatten)(k.exports):[]),O=w.next();!O.done;O=w.next()){var x=O.value;(0,d.default)(x,t)}}catch(e){r={error:e}}finally{try{O&&!O.done&&(i=w.return)&&i.call(w)}finally{if(r)throw r.error}}var j=c.default.config.get("mockNgDefResolver").pop();return _||c.default.config.delete("mockNgDefResolver"),[g,k,j]}},3877:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},2937:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockPipes=function(){for(var e=[],t=0;t=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(8773)),i=o(r(1184)),l=r(5974),u=function(e,t){var r=" ".concat(function(e,t){return"i"===t?"[".concat(e,"]"):"(".concat(e,")")}(e,t),'="');return(r+="i"===t?e:"__ngMocksOutput('".concat(e,"', $event)"))+'"'},f=function(e,t,r){var o,a;if(!e&&"o"===r)return"";var l="",f=null!=e?e:t;try{for(var c=n(t),d=c.next();!d.done;d=c.next()){var s=d.value,v=(0,i.default)(s),p=v.name,h=v.alias;l+=-1===f.indexOf(h||p)?"":u(h||p,r)}}catch(e){o={error:e}}finally{try{d&&!d.done&&(a=c.return)&&a.call(c)}finally{if(o)throw o.error}}return l};t.default=function(e,t){var r=t.selector,n=t.bindings,o=t.inputs,i=t.outputs,u="";return"string"==typeof e?u=e:(0,l.isNgDef)(e,"p")&&n&&-1!==n.indexOf("$implicit")?u="{{ $implicit | ".concat((0,a.default)(e).name," }}"):r&&(u+="<".concat(r),u+=f(n,o,"i"),u+=f(n,i,"o"),u+=">")),u}},2091:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=i(r(3174)),u=i(r(5006)),f=i(r(445)),c=function(e,t,r){var n=function(){if("function"==typeof r[e]){if(t["__ngMocks_".concat(e,"__origin")]!==r[e]){var n=f.default.createClone(r[e],t,r);(0,l.default)(t,"__ngMocks_".concat(e),n),(0,l.default)(t,"__ngMocks_".concat(e,"__origin"),r[e])}return t["__ngMocks_".concat(e)]}return r[e]};return(0,l.default)(n,"__ngMocksProxy",!0),n},d=function(e,t,r){var n=function(n){t["__ngMocks_".concat(e)]&&(t["__ngMocks_".concat(e)]=void 0),t["__ngMocks_".concat(e,"__origin")]&&(t["__ngMocks_".concat(e,"__origin")]=void 0),r[e]=n};return(0,l.default)(n,"__ngMocksProxy",!0),n};t.default=function(e,t,r,i){var s,v;if(void 0===i&&(i=!1),t){(0,l.default)(e,"__ngMocks__source",t);var p,h=(p=e,o(o([],n(Object.getOwnPropertyNames(p)),!1),n(Object.keys(p)),!1)),y=o(o([],n(function(e){return o(o(o([],n(f.default.extractPropertiesFromPrototype(Object.getPrototypeOf(e))),!1),n(f.default.extractMethodsFromPrototype(Object.getPrototypeOf(e))),!1),n(Object.keys(e)),!1)}(t)),!1),n(r),!1);try{for(var _=a(y),g=_.next();!g.done;g=_.next()){var b=g.value;(i||-1===h.indexOf(b))&&((0,u.default)(e,b,{get:c(b,e,t),set:d(b,e,t)}),h.push(b))}}catch(e){s={error:e}}finally{try{g&&!g.done&&(v=_.return)&&v.call(_)}finally{if(s)throw s.error}}}}},6086:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},l=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var u=r(860),f=r(2603),c=l(r(3174)),d=r(6456),s=l(r(8862)),v=r(5974);t.default=function(e){var t,r,l;if(!(0,v.isNgDef)(e,"c")&&!(0,v.isNgDef)(e,"d"))return{};var p=(0,s.default)(e),h={};try{for(var y=i(Object.keys(p)),_=y.next();!_.done;_=y.next()){var g=_.value;"standalone"!==g?h[g]=p[g]:(0,c.default)(h,"__ngMocksStandalone",!!p[g])}}catch(e){t={error:e}}finally{try{_&&!_.done&&(r=y.return)&&r.call(y)}finally{if(t)throw t.error}}return h.selector&&/[\s,[\]]/.test(h.selector)&&(h.selector=""),h.selector||(h.selector=(null===(l=f.TestBed.ngMocksSelectors)||void 0===l?void 0:l.get(e))||"",h.selector||(h.selector="ng-mocks-".concat(e.name),function(e,t){var r,i=(0,d.extendClass)(e),l={provide:e,useExisting:i};t.providers=a(a([],o(t.providers||[]),!1),[l],!1);var c={};try{var s=f.TestBed.ngMocksOverrides.get(e).override;(c=n({},s.set)).providers=c.providers?a(a([],o(c.providers),!1),[l],!1):t.providers}catch(e){}var p=!0===t.__ngMocksStandalone;((0,v.isNgDef)(e,"c")?u.Component:u.Directive)(n(n(n({},t),c),p?{standalone:p}:{}))(i),f.TestBed.configureTestingModule(((r={})[p?"imports":"declarations"]=[i],r))}(e,h),f.TestBed.ngMocksSelectors&&f.TestBed.ngMocksSelectors.set(e,h.selector))),h}},8406:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockRenderFactory=function(e,t,r){void 0===r&&(r={}),(0,f.default)(e,"MockRender");var n="string"==typeof e||(0,c.isNgDef)(e,"t")?{}:(0,g.default)(e),o=(0,y.default)(e,n,t,r),a=m(o,t,e,r);return"root"!==d.default.current().level&&!1!==r.configureTestBed&&a.configureTestBed(),a};var o=r(860),a=r(2603),i=n(r(3174)),l=r(6456),u=n(r(2970)),f=n(r(6804)),c=r(5974),d=n(r(7592)),s=n(r(8073)),v=r(6189),p=n(r(5006)),h=r(5779),y=n(r(3078)),_=n(r(2091)),g=n(r(6086)),b=["Forgot to flush TestBed?","MockRender cannot be used without a reset after TestBed.get / TestBed.inject / TestBed.createComponent and another MockRender in the same test.","If you want to mock a service before rendering, consider usage of MockRenderFactory or MockInstance.","To flush TestBed, add a call of ngMocks.flushTestBed() before the call of MockRender, or pass `reset: true` to MockRender options."].join(" "),m=function(e,t,r,n){var f=function(n,d){f.configureTestBed();var y=a.TestBed.createComponent(e);return(0,_.default)(y.componentInstance,null!=n?n:{},null!=t?t:[]),(0,i.default)(y,"ngMocksStackId",s.default.global.get("bullet:stack:id")),(void 0===d||d)&&y.detectChanges(),"string"==typeof r||(0,c.isNgDef)(r,"c")||(0,c.isNgDef)(r,"d")||e.tpl&&(0,c.isNgDef)(r,"p")?function(e,t,r){e.point=e.debugElement.children[0]&&"#text"!==e.debugElement.children[0].nativeElement.nodeName&&"#comment"!==e.debugElement.children[0].nativeElement.nodeName?e.debugElement.children[0]:e.debugElement,(0,c.isNgDef)(t,"d")?(0,p.default)(e.point,"componentInstance",{get:function(){return v.ngMocks.get(e.point,t)}}):(0,c.isNgDef)(t,"p")&&(0,p.default)(e.point,"componentInstance",{get:function(){return v.ngMocks.findInstance(e.point,t)}}),function(e,t){if(e)try{t()}catch(e){}}(!r,(function(){return(0,_.default)(e.componentInstance,e.point.componentInstance,[])}))}(y,r,n):function(e,t,r){var n;try{n=(0,l.getInjection)(t)}catch(e){if((0,c.isNgDef)(t,"p"))throw new Error(["Cannot render ".concat((0,u.default)(t),"."),"Did you forget to set $implicit param, or add the pipe to providers?","https://ng-mocks.sudo.eu/guides/pipe"].join(" "));throw e}r&&v.ngMocks.stub(n,r),e.point=(0,h.MockService)(o.DebugElement,{childNodes:[],children:[],componentInstance:n,nativeElement:(0,h.MockService)(HTMLElement)}),(0,_.default)(e.componentInstance,e.point.componentInstance,[],!0)}(y,r,n),y};return f.declaration=e,f.bindings=t,f.configureTestBed=function(e,t){return function(){var r,n=(0,a.getTestBed)(),o=(null===(r=n._compiler)||void 0===r?void 0:r.declarations)||n.declarations||n._declarations;if(!o||-1===o.indexOf(e)){!function(e){var t=s.default.global.get("flags"),r=(0,a.getTestBed)();e.reset||!r._instantiated&&!r._testModuleRef?v.ngMocks.flushTestBed():"throw"!==t.onTestBedFlushNeed&&(r._instantiated||r._testModuleRef)&&("warn"===t.onTestBedFlushNeed&&console.warn(b),v.ngMocks.flushTestBed())}(t);try{var l=[];e.providers&&l.push(e.providers),l.push(e),a.TestBed.configureTestingModule({declarations:l})}catch(e){!function(e){var t=new Error(b);throw(0,i.default)(t,"parent",e),t}(e)}}}}(e,n),f}},1661:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0)return!0;var n=e.codePointAt(0);if(n&&n>=65&&n<=90&&null!==t.match(/\bthis\./gm))return!0;var o=new RegExp("\\(this,\\s*".concat(e,"\\)"),"mg");return null!==t.match(o)}(r[1],t,e)}},402:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return null!==e&&"object"==typeof e&&"InjectionToken"!==e.ngMetadataName&&"object"==typeof Object.getPrototypeOf(e)}},1365:function(e,t,r){var n=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},o=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=i(r(5006)),u=i(r(7331)),f=i(r(7794)),c=i(r(5020));t.default=function(e,t,r,i){var d,s,v=function(){for(var n=[],o=0;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(3174)),i=o(r(2970)),l=o(r(445));t.default=function(e){var t,r,o,u,f=(0,i.default)(e),c={};(0,a.default)(c,"__ngMocks",!0);var d=l.default.extractMethodsFromPrototype(e);try{for(var s=n(d),v=s.next();!v.done;v=s.next()){var p=v.value;l.default.mock(c,p,f)}}catch(e){t={error:e}}finally{try{v&&!v.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}var h=l.default.extractPropertiesFromPrototype(e);try{for(var y=n(h),_=y.next();!_.done;_=y.next()){var g=_.value;l.default.mock(c,g,"get",f),l.default.mock(c,g,"set",f)}}catch(e){o={error:e}}finally{try{_&&!_.done&&(u=y.return)&&u.call(y)}finally{if(o)throw o.error}}return Object.setPrototypeOf(c,e),c}},5006:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(2970)),i=["sanitize","bypassSecurityTrustHtml","bypassSecurityTrustStyle","bypassSecurityTrustScript","bypassSecurityTrustUrl","bypassSecurityTrustResourceUrl"],l={DomSanitizer:i,Sanitizer:i},u=function(e){var t,r,o,i=Object.getOwnPropertyNames(e);try{for(var u=n(null!==(o=l[(0,a.default)(e)])&&void 0!==o?o:[]),f=u.next();!f.done;f=u.next()){var c=f.value;i.push(c)}}catch(e){t={error:e}}finally{try{f&&!f.done&&(r=u.return)&&r.call(u)}finally{if(t)throw t.error}}return i};t.default=function(e){for(var t,r,o=[],a=e;a&&null!==Object.getPrototypeOf(a);){try{for(var i=(t=void 0,n(u(a))),l=i.next();!l.done;l=i.next()){var f=l.value;if("constructor"!==f){var c=Object.getOwnPropertyDescriptor(a,f);c&&(c.get||c.set)||-1!==o.indexOf(f)||o.push(f)}}}catch(e){t={error:e}}finally{try{l&&!l.done&&(r=i.return)&&r.call(i)}finally{if(t)throw t.error}}a=Object.getPrototypeOf(a)}return o}},7794:function(e,t){var r=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")};Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){for(var t,n,o=[],a=e;a&&null!==Object.getPrototypeOf(a);){try{for(var i=(t=void 0,r(Object.getOwnPropertyNames(a))),l=i.next();!l.done;l=i.next()){var u=l.value;if("constructor"!==u){var f=Object.getOwnPropertyDescriptor(a,u);f&&(f.get||f.set)&&-1===o.indexOf(u)&&o.push(u)}}}catch(e){t={error:e}}finally{try{l&&!l.done&&(n=i.return)&&n.call(i)}finally{if(t)throw t.error}}a=Object.getPrototypeOf(a)}return o}},5020:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){for(var r=e;r&&null!==Object.getPrototypeOf(r);){var n=Object.getOwnPropertyDescriptor(r,t);if(n)return n;r=Object.getPrototypeOf(r)}}},8536:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=n(r(3174)),a=function(e,t){void 0===t&&(t=!1);var r,n,i=a.customMockFunction&&!t?a.customMockFunction(e):function(e){return n&&n(e),r};return(0,o.default)(i,"__ngMocks",!0),(0,o.default)(i,"__ngMocksSet",(function(e){return n=e})),(0,o.default)(i,"__ngMocksGet",(function(e){return r=e})),i};t.default=a},445:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.registerMockFunction=function(e){(0,o.default)().ngMockshelperMockService.registerMockFunction(e)};var o=n(r(1102)),a=n(r(1365)),i=n(r(3886)),l=n(r(5006)),u=n(r(7331)),f=n(r(7794)),c=n(r(5020)),d=n(r(4235)),s=n(r(8536)),v=n(r(2004)),p=n(r(3663)),h=n(r(9465));(0,o.default)().ngMockshelperMockService=(0,o.default)().ngMockshelperMockService||{mockFunction:s.default,registerMockFunction:function(e){(0,o.default)().ngMockshelperMockService.mockFunction.customMockFunction=e},createClone:a.default,createMockFromPrototype:i.default,definePropertyDescriptor:l.default,extractMethodsFromPrototype:u.default,extractPropertiesFromPrototype:f.default,extractPropertyDescriptor:c.default,mock:d.default,replaceWithMocks:v.default,resolveProvider:p.default,useFactory:h.default},t.default=(0,o.default)().ngMockshelperMockService},4235:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0&&"get"!==e[0]&&"set"!==e[0]?r=e[0]:e.length>0&&("get"===e[0]||"set"===e[0])&&(t=e[0],r=e[1]),{accessType:t,mockName:r}}(r),u=l.accessType,f=l.mockName,c=Object.getOwnPropertyDescriptor(e,t);if(c&&c[u||"value"])return c[u||"value"];var d=function(e,t,r,n){return"".concat(null!=t?t:"function"==typeof r.prototype?r.prototype.name:(0,a.default)(r),".").concat(e).concat(null!=n?n:"")}(t,f,e,u),s=i.default.mockFunction(d,!!u),v=function(e,t,r){var o;return n(n(n(n({},"get"===r&&e&&e.set?{set:e.set}:{}),"set"===r&&e&&e.get?{get:e.get}:{}),r?{}:{writable:!0}),((o={})[r||"value"]=t,o.configurable=!0,o.enumerable=!0,o))}(c,s,u);return v.get&&v.set&&v.get.__ngMocks&&v.set.__ngMocks&&v.set.__ngMocksSet((function(e){return v.get.__ngMocksGet(e)})),Object.defineProperty(e,t,v),s}},2004:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=r(1763),u=r(5974),f=i(r(8073)),c=["canActivate","canActivateChild","canDeactivate","canMatch","canLoad"],d=function(e,t){return Array.isArray(e[t])?function(e){var t,r,n=[];try{for(var a=o(e),i=a.next();!i.done;i=a.next()){var c=i.value;!f.default.isProvidedDef(c)&&f.default.isExcludedDef(l.NG_MOCKS_GUARDS)||(n.push(c),(0,u.isNgDef)(c)||f.default.touches.add(c))}}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}return n}(e[t]):e[t]},s=function(e,t){var r,i,v;if(f.default.cacheDeclarations.has(e))return f.default.cacheDeclarations.get(e);if("object"!=typeof e)return e;if(t.has(e))return e;var p=!1;return Array.isArray(e)?(r=a(function(e,t,r){var n,a,i=[],l=!1;e.set(t,i);try{for(var u=o(t),c=u.next();!c.done;c=u.next()){var d=c.value;f.default.isExcludedDef(d)?l=l||!0:(i.push(r(d,e)),l=l||i[i.length-1]!==d)}}catch(e){n={error:e}}finally{try{c&&!c.done&&(a=u.return)&&a.call(u)}finally{if(n)throw n.error}}return[l,i]}(t,e,s),2),p=r[0],v=r[1]):e&&(i=a(function(e,t,r){var a,i,s,v,p,h,y,_={},g=!1;e.set(t,_);try{for(var b=o(Object.keys(t)),m=b.next();!m.done;m=b.next()){var M=m.value;f.default.isExcludedDef(t[M])?g=g||!0:(_[M]=r(t[M],e),g=g||_[M]!==t[M])}}catch(e){a={error:e}}finally{try{m&&!m.done&&(i=b.return)&&i.call(b)}finally{if(a)throw a.error}}try{for(var k=o(c),w=k.next();!w.done;w=k.next()){var O=w.value,x=d(_,O);x&&_[O].length!==x.length&&(g=g||!0,_=n(n({},_),((p={})[O]=x,p)))}}catch(e){s={error:e}}finally{try{w&&!w.done&&(v=k.return)&&v.call(k)}finally{if(s)throw s.error}}if("object"==typeof _.resolve&&_.resolve){var j={},D=!1;try{for(var S=o(Object.keys(_.resolve)),P=S.next();!P.done;P=S.next()){M=P.value;var C=_.resolve[M];f.default.isProvidedDef(C)||!f.default.isExcludedDef(l.NG_MOCKS_RESOLVERS)?(j[M]=C,(0,u.isNgDef)(C)||f.default.touches.add(C)):D=D||!0}}catch(e){h={error:e}}finally{try{P&&!P.done&&(y=S.return)&&y.call(S)}finally{if(h)throw h.error}}D&&(g=g||!0,_=n(n({},_),{resolve:j}))}return[g,_]}(t,e,s),2),p=i[0],v=i[1]),p?(Object.setPrototypeOf(v,Object.getPrototypeOf(e)),v):e};t.default=function(e){var t=new Map,r=s(e,t);return t.clear(),r}},3663:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=r(6456),l=r(1763),u=a(r(7285)),f=a(r(6297)),c=r(4152),d=a(r(8073)),s=a(r(445)),v=a(r(2415)),p=function(e,t,r){var n=!1,a=!e;return t&&e&&!a&&(a=function(e,t){for(var r,n,a=[],i=2;i=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var i=a(r(3295)),l=a(r(6297)),u=r(4152),f=a(r(8073)),c=a(r(5006)),d=a(r(5020)),s=a(r(9465)),v=r(5779),p=i.default.neverMockProvidedFunction,h=i.default.neverMockToken,y=[[function(e){return"boolean"==typeof e},!1],[function(e){return"number"==typeof e},0],[function(e){return"string"==typeof e},""],[function(e){return null===e},null]],_=function(e,t,r){var a,i;return t===e?r?(0,s.default)(e,(function(){})):void 0:e.multi?void(null===(a=f.default.config.get("ngMocksMulti"))||void 0===a||a.add(t)):(-1!==Object.keys(e).indexOf("useValue")?i=function(e,t){return(0,s.default)(t,(function(){return e.useValue&&"object"==typeof e.useValue?(0,v.MockService)(e.useValue):function(e){var t,r;try{for(var a=n(y),i=a.next();!i.done;i=a.next()){var l=o(i.value,2),u=l[0],f=l[1];if(u(e))return f}}catch(e){t={error:e}}finally{try{i&&!i.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}}(e.useValue)}))}(e,t):-1!==Object.keys(e).indexOf("useExisting")?i=e:-1!==Object.keys(e).indexOf("useClass")?i=function(e,t){return f.default.builtProviders.has(e.useClass)&&f.default.builtProviders.get(e.useClass)===e.useClass?e:(0,s.default)(t,(function(){return(0,v.MockService)(e.useClass)}))}(e,t):-1!==Object.keys(e).indexOf("useFactory")&&(i=(0,s.default)(t,(function(){return{}}))),i)};t.default=function(e,t){void 0===t&&(t=!1);var r=(0,l.default)(e);if("mock"===f.default.getResolution(r));else{if(function(e){return"function"==typeof e&&-1!==p.indexOf(e.name)}(r))return e;if(function(e){return(0,u.isNgInjectionToken)(e)&&-1!==h.indexOf(e.toString())}(r))return}var o=f.default.flags.has("cacheProvider")?f.default.cacheProviders:void 0;return r===e&&o&&o.has(r)?o.get(r):function(e,t,r){var o;return"function"==typeof t&&(o=function(e,t){return(0,s.default)(t,(function(){var r=(0,v.MockService)(t);return t!==e&&-1!==Object.keys(e).indexOf("useClass")&&function(e,t){var r,o,a=Object.getOwnPropertyNames(e),i=(0,v.MockService)(t);try{for(var l=n(Object.getOwnPropertyNames(i)),u=l.next();!u.done;u=l.next()){var f=u.value;if(-1===a.indexOf(f)){var s=(0,d.default)(i,f);(0,c.default)(e,f,s)}}}catch(e){r={error:e}}finally{try{u&&!u.done&&(o=l.return)&&o.call(l)}finally{if(r)throw r.error}}}(r,e.useClass),r}))}(e,t)),t===e&&o&&r&&r.set(t,o),o}(e,r,o)||_(e,r,t)}},5779:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.MockService=function(e){for(var t=[],r=1;r0&&"string"==typeof t[0]?t[0]:t[1],o=t.length>0&&t[0]&&"object"==typeof t[0]?t[0]:void 0,a=new Map,i=v(a,e,n,o);return a.clear(),i};var i=a(r(2970)),l=a(r(8195)),u=a(r(96)),f=a(r(2956)),c=a(r(402)),d=a(r(445)),s=[[u.default,function(e,t){var r=d.default.createMockFromPrototype(t.prototype);return e.set(t,r),r}],[f.default,function(e,t,r){var n=d.default.mockFunction("func:".concat(r||(0,i.default)(t)));return e.set(t,n()),n}],[function(e){return Array.isArray(e)},function(){return[]}],[c.default,function(e,t,r,o){var a,i,l=d.default.createMockFromPrototype(t.constructor.prototype);e.set(t,l);try{for(var u=n(Object.keys(t)),f=u.next();!f.done;f=u.next()){var c=f.value,s=o(e,t[c],"".concat(r||"instance",".").concat(c));void 0!==s&&(l[c]=s)}}catch(e){a={error:e}}finally{try{f&&!f.done&&(i=u.return)&&i.call(u)}finally{if(a)throw a.error}}return Object.setPrototypeOf(l,Object.getPrototypeOf(t)),l}]],v=function(e,t,r,a){void 0===r&&(r="");var i=function(e,t,r,a){var i,l,u;try{for(var f=n(s),c=f.next();!c.done;c=f.next()){var d=o(c.value,2),v=d[0],p=d[1];if(v(t))return null!==(u=e.get(t))&&void 0!==u?u:p(e,t,r,a)}}catch(e){i={error:e}}finally{try{c&&!c.done&&(l=f.return)&&l.call(f)}finally{if(i)throw i.error}}}(e,t,r,v);return a&&(0,l.default)(i,a),i}},534:function(e,t){Object.defineProperty(t,"__esModule",{value:!0})},9715:function(e,t,r){var n=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var a=o(r(2023)),i=r(6456),l=o(r(7285)),u=o(r(6297)),f=r(1433),c=o(r(445)),d=o(r(7112)),s=o(r(3309)),v=function(e,t,r,n){var o=function(e,t){var r=(0,u.default)(t);return a.default.NG_VALIDATORS&&r===a.default.NG_VALIDATORS?(0,s.default)(r,(function(){return new f.MockValidatorProxy(e)})):a.default.NG_ASYNC_VALIDATORS&&r===a.default.NG_ASYNC_VALIDATORS?(0,s.default)(r,(function(){return new f.MockAsyncValidatorProxy(e)})):a.default.NG_VALUE_ACCESSOR&&r===a.default.NG_VALUE_ACCESSOR?(0,s.default)(r,(function(){return new f.MockControlValueAccessorProxy(e)})):void 0}(t,r);if(o)return o;var i=function(e,t,r){var n=(0,u.default)(r);if(n!==a.default.NgControl&&n!==a.default.FormControlDirective)return r!==n&&(0,l.default)(r.useExisting)===e?(0,d.default)(n,t):void 0}(e,t,r);return i||c.default.resolveProvider(r,n)};t.default=function(e,t,r,o){var l,f,c,d=[];try{for(var s=n((0,i.flatten)(r||[])),p=s.next();!p.done;p=s.next()){var h=p.value;(0,u.default)(h)===a.default.NG_VALUE_ACCESSOR&&(c=!1);var y=v(e,t,h,o);y&&d.push(y)}}catch(e){l={error:e}}finally{try{p&&!p.done&&(f=s.return)&&f.call(s)}finally{if(l)throw l.error}}return{providers:d,setControlValueAccessor:c}}},6847:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},a=this&&this.__values||function(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},i=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var l=i(r(5551)),u=i(r(5756)),f=i(r(615)),c=i(r(5555)),d=i(r(8385)),s=i(r(8073)),v=i(r(224)),p=i(r(445)),h=i(r(9715)),y=i(r(7112));t.default=function(e,t,r,i){var _,g,b,m,M=s.default.config.has("mockNgDefResolver");M||s.default.config.set("mockNgDefResolver",new l.default);var k=n({},i);if(void 0!==r.exportAs&&(k.exportAs=r.exportAs),void 0!==r.selector&&(k.selector=r.selector),void 0!==r.standalone&&(k.standalone=r.standalone),r.standalone&&r.imports){var w=o((0,v.default)({imports:r.imports,skipExports:!0}),2)[1].imports;(null==w?void 0:w.length)&&(k.imports=w)}if(r.hostDirectives){var O=o((0,v.default)({hostDirectives:r.hostDirectives,skipExports:!0}),2)[1].hostDirectives;(null==O?void 0:O.length)&&(k.hostDirectives=O)}var x=(0,h.default)(e,t,r.providers||[],s.default.config.get("mockNgDefResolver")),j=x.setControlValueAccessor,D=x.providers;D.push((0,y.default)(e,t)),k.providers=D;var S=(0,h.default)(e,t,r.viewProviders||[],s.default.config.get("mockNgDefResolver")).providers;S.length>0&&(k.viewProviders=S);var P=function(e,t,r){return{config:s.default.config.get(e),outputs:t.outputs,queryScanKeys:[],setControlValueAccessor:r}}(e,r,null!=j?j:-1!==p.default.extractMethodsFromPrototype(e.prototype).indexOf("writeValue"));(0,f.default)(t,e,P),r.queries&&(0,u.default)(t,r.inputs,Object.keys(r.queries)),(0,c.default)(t,r.outputs),P.queryScanKeys=(0,d.default)(t,r.queries),P.hostBindings=[];try{for(var C=a(r.hostBindings||[]),N=C.next();!N.done;N=C.next()){var T=o(N.value,1)[0];-1===P.hostBindings.indexOf(T)&&P.hostBindings.push(T)}}catch(e){_={error:e}}finally{try{N&&!N.done&&(g=C.return)&&g.call(C)}finally{if(_)throw _.error}}P.hostListeners=[];try{for(var E=a(r.hostListeners||[]),A=E.next();!A.done;A=E.next())T=o(A.value,1)[0],-1===P.hostListeners.indexOf(T)&&P.hostListeners.push(T)}catch(e){b={error:e}}finally{try{A&&!A.done&&(m=E.return)&&m.call(E)}finally{if(b)throw b.error}}return M||s.default.config.delete("mockNgDefResolver"),k}},6932:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(6456),a=n(r(6804)),i=r(6763),l=n(r(8073)),u=n(r(5937));t.default=function(e,t,r,n,f,c){if((0,a.default)(e,r),(0,i.isMockNgDef)(e,t))return e;if(l.default.flags.has(n)&&l.default.cacheDeclarations.has(e))return(0,u.default)(e);var d=l.default.config.has("ngMocksDepsResolution");d||l.default.config.set("ngMocksDepsResolution",new Map);var s=(0,o.extendClass)(f);return c(e,s),l.default.flags.has(n)&&l.default.cacheDeclarations.set(e,s),d||l.default.config.delete("ngMocksDepsResolution"),s}},7610:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(6739),a=n(r(8073));t.default=function(e,t){var r,n=(0,o.getSourceOfMock)(e),i=null!==(r=a.default.configInstance.get(n))&&void 0!==r?r:{__set:!0};i.exported||(i.exported=new Set),t&&i.exported.add((0,o.getSourceOfMock)(t)),i.__set&&(i.__set=void 0,a.default.configInstance.set(n,i))}},5937:function(e,t,r){var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(1763),a=n(r(8073)),i=n(r(5687));t.default=function(e){var t;try{t=(0,i.default)().debugElement.injector.get(o.NG_MOCKS).get(e)}catch(e){}return t||(t=a.default.cacheDeclarations.get(e)),e.__ngMocksResolutions&&a.default.config.has("mockNgDefResolver")&&a.default.config.get("mockNgDefResolver").merge(e.__ngMocksResolutions),t}},7112:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){return{provide:e,useExisting:t}}},3309:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){return{multi:!0,provide:e,useFactory:t}}},4673:function(e,t,r){var n=this&&this.__assign||function(){return n=Object.assign||function(e){for(var t,r=1,n=arguments.length;r=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")},a=this&&this.__read||function(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,o,a=r.call(e),i=[];try{for(;(void 0===t||t-- >0)&&!(n=a.next()).done;)i.push(n.value)}catch(e){o={error:e}}finally{try{n&&!n.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i},i=this&&this.__spreadArray||function(e,t,r){if(r||2===arguments.length)for(var n,o=0,a=t.length;o{t.d(o,{A:()=>r});const r={flags:["cacheModule","cacheComponent","cacheDirective","cacheProvider","correctModuleExports"],mockRenderCacheSize:25,neverMockModule:["ApplicationModule","CommonModule","BrowserModule","_ApplicationModule","_CommonModule","_BrowserModule"],neverMockProvidedFunction:["DomRendererFactory2","EventManager","Injector","RendererFactory2","Sanitizer","DomSanitizer","DomSanitizerImpl","ApplicationInitStatus","ApplicationRef","Compiler","IterableDiffers","KeyValueDiffers","_DomRendererFactory2","_EventManager","_Injector","_Sanitizer","_DomSanitizer","_DomSanitizerImpl","_ApplicationInitStatus","_ApplicationRef","_Compiler","_IterableDiffers","_KeyValueDiffers"],neverMockToken:["InjectionToken Set Injector scope.","InjectionToken EventManagerPlugins","InjectionToken HammerGestureConfig","InjectionToken AppId","InjectionToken DefaultCurrencyCode","InjectionToken LocaleId","InjectionToken SCHEDULER_TOKEN"],onMockBuilderMissingDependency:"throw",onMockInstanceRestoreNeed:"warn",onTestBedFlushNeed:"warn",dependencies:["declarations","hostDirectives","entryComponents","bootstrap","providers","viewProviders","imports","exports"]}},174:(e,o,t)=>{t.d(o,{A:()=>n});var r=t(932);const n=(e,o,t,n=!1)=>{r.A.definePropertyDescriptor(e,o,{configurable:!0,enumerable:n,value:t,writable:!0})}},456:(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{Ah:()=>getInjection,Bq:()=>flatten,He:()=>extendClass,LG:()=>mapValues,Nn:()=>mapEntries,by:()=>extractDependency,d4:()=>mapKeys,d5:()=>getTestBedInjection});var _angular_core_testing__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(957),_core_define_property__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(174),_core_reflect_parameters_resolve__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(749),_func_get_global__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(102),_func_get_name__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__(970),_ng_mocks_universe__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(73);const getTestBedInjection=e=>{try{return getInjection(e)}catch(e){return}},getInjection=e=>{const o=(0,_angular_core_testing__WEBPACK_IMPORTED_MODULE_0__.getTestBed)();return o.inject?o.inject(e):o.get(e)},flatten=(e,o=[])=>{if(Array.isArray(e))for(const t of e)flatten(t,o);else if(null!==e&&"object"==typeof e&&Array.isArray(e.ɵproviders))for(const t of e.ɵproviders)flatten(t,o);else o.push(e);return o},mapKeys=e=>{const o=[];return e.forEach(((e,t)=>o.push(t))),o},mapValues=(e,o)=>{const t=[];return o?e.forEach((e=>{o.add(e)})):e.forEach((e=>{t.push(e)})),t},mapEntries=(e,o)=>{const t=[];return o?e.forEach(((e,t)=>o.set(t,e))):e.forEach(((e,o)=>t.push([o,e]))),t},extractDependencyArray=(e,o)=>{for(const t of e){const e=t&&"object"==typeof t?t.ngMetadataName:void 0;"Optional"!==e&&"SkipSelf"!==e&&"Self"!==e&&o.add(t)}},extractDependency=(e,o)=>{if(o)for(const t of e)Array.isArray(t)?extractDependencyArray(t,o):o.add(t)},extendClassicClass=base=>{let child;const index=_ng_mocks_universe__WEBPACK_IMPORTED_MODULE_3__.A.index(),glb=(0,_func_get_global__WEBPACK_IMPORTED_MODULE_4__.A)();glb.ngMocksParent=base;try{eval(`\n var glb = typeof window === 'undefined' ? global : window;\n class MockMiddleware${index} extends glb.ngMocksParent {};\n glb.ngMocksResult = MockMiddleware${index};\n `),child=glb.ngMocksResult}catch(e){class o extends glb.ngMocksParent{}child=o}finally{glb.ngMocksResult=void 0,glb.ngMocksParent=void 0}return(0,_core_define_property__WEBPACK_IMPORTED_MODULE_1__.A)(child.prototype,`__ngMocks_index_${index}`,void 0,!1),child},extendClass=e=>{const o=extendClassicClass(e);(0,_core_define_property__WEBPACK_IMPORTED_MODULE_1__.A)(o,"name",`MockMiddleware${(0,_func_get_name__WEBPACK_IMPORTED_MODULE_5__.A)(e)}`,!0);const t=(0,_core_reflect_parameters_resolve__WEBPACK_IMPORTED_MODULE_2__.A)(e);return t.length>0&&(0,_core_define_property__WEBPACK_IMPORTED_MODULE_1__.A)(o,"parameters",[...t]),o}},749:(e,o,t)=>{t.d(o,{A:()=>n});var r=t(673);const n=e=>{var o;return null!==(o=(0,r.A)(e).parameters)&&void 0!==o?o:[]}},763:(e,o,t)=>{t.d(o,{C3:()=>i,Em:()=>s,So:()=>c,ZG:()=>_,en:()=>n,gG:()=>l,rO:()=>a});var r=t(614);const n=new r.InjectionToken("NG_MOCKS");n.__ngMocksSkip=!0;const s=new r.InjectionToken("NG_MOCKS_TOUCHES");s.__ngMocksSkip=!0;const c=new r.InjectionToken("NG_MOCKS_OVERRIDES");c.__ngMocksSkip=!0;const _=new r.InjectionToken("NG_MOCKS_GUARDS");_.__ngMocksSkip=!0;const i=new r.InjectionToken("NG_MOCKS_RESOLVERS");i.__ngMocksSkip=!0;const a=new r.InjectionToken("NG_MOCKS_INTERCEPTORS");a.__ngMocksSkip=!0;const l=new r.InjectionToken("NG_MOCKS_ROOT_PROVIDERS");l.__ngMocksSkip=!0},439:(e,o,t)=>{function r({name:e,alias:o,required:t},r=!1){return t?{name:e,alias:o,required:t}:o&&e!==o?r?o:`${e}:${o}`:r?"":e}t.d(o,{A:()=>r})},184:(e,o,t)=>{function r(e){if("string"==typeof e){const[o,t]=e.split(":").map((e=>e.trim()));return o!==t&&t?{name:o,alias:t}:{name:o}}return e}t.d(o,{A:()=>r})},285:(e,o,t)=>{t.d(o,{A:()=>r});const r=e=>"function"==typeof e&&e.__forward_ref__?e():e},102:(e,o,t)=>{t.d(o,{A:()=>r});const r=()=>"undefined"==typeof window?t.g:window},970:(e,o,t)=>{t.d(o,{A:()=>n});const r=new RegExp("[^0-9a-z]+","mgi"),n=e=>{let o;return"function"==typeof e&&e.name?o=e.name:"function"==typeof e?o="arrowFunction":"object"==typeof e&&e&&"InjectionToken"===e.ngMetadataName?o=e._desc:"object"==typeof e&&e&&"function"==typeof e.constructor&&(o=e.constructor.name),o||(o="unknown"),o.replace(r,"_")}},297:(e,o,t)=>{t.d(o,{A:()=>n});var r=t(659);const n=e=>e&&"object"==typeof e&&e.provide?e.provide:(0,r.h)(e)?e.ngModule:e&&"object"==typeof e&&e.directive?e.directive:e},218:(e,o,t)=>{t.d(o,{p:()=>p});var r=t(152),n=t(673);const s=(e,o)=>{const{decorators:t}=(0,n.A)(e);if(0===t.length)return!1;let r=1;if("Injectable"===o&&-1!==t.indexOf("Injectable"))return!0;for(;"Injectable"===t[t.length-r];)r+=1;return t[t.length-r]===o},c=(e,o)=>(!o||"m"===o)&&s(e,"NgModule"),_=(e,o)=>(!o||"c"===o)&&s(e,"Component"),i=(e,o)=>(!o||"d"===o)&&s(e,"Directive"),a=(e,o)=>(!o||"p"===o)&&s(e,"Pipe"),l=(e,o)=>(!o||"i"===o)&&s(e,"Injectable");function p(e,o){if("t"===o)return(0,r.S)(e);if("function"!=typeof e)return!1;const t=c(e,o),n=_(e,o),s=i(e,o),p=a(e,o),u=l(e,o);return t||n||s||p||u}},152:(e,o,t)=>{t.d(o,{S:()=>r});const r=e=>e&&"object"==typeof e&&"InjectionToken"===e.ngMetadataName},659:(e,o,t)=>{t.d(o,{h:()=>r});const r=e=>e&&"object"==typeof e&&"function"==typeof e.ngModule},73:(e,o,t)=>{t.d(o,{A:()=>p});var r=t(295),n=t(102),s=t(970);const c=e=>()=>(_.global.has(e)||_.global.set(e,new Map),_.global.get(e));(0,n.A)().ngMocksUniverse=(0,n.A)().ngMocksUniverse||{};const _=(0,n.A)().ngMocksUniverse;_.builtDeclarations=new Map,_.builtProviders=new Map,_.cacheDeclarations=new Map,_.cacheProviders=new Map,_.config=new Map,_.configInstance=new Map,_.flags=new Set(r.A.flags),_.global=new Map,_.touches=new Set,_.global.set("flags",{onMockBuilderMissingDependency:r.A.onMockBuilderMissingDependency,onMockInstanceRestoreNeed:r.A.onMockInstanceRestoreNeed,onTestBedFlushNeed:r.A.onTestBedFlushNeed}),_.getOverrides=c("overrides"),_.getDefaults=c("defaults"),_.getConfigMock=c("configMock");const i=e=>{{const o=_.getDefaults().get(e);if(o)return o}{const o="function"==typeof e?_.getDefaults().get(`@${(0,s.A)(e)}`):void 0;if(o)return o}return[]};_.getResolution=e=>{const o=_.config.get("ngMocksDepsResolution");if(null==o?void 0:o.has(e))return o.get(e);const[t]=i(e);return t},_.getBuildDeclaration=e=>{if(_.builtDeclarations.has(e))return _.builtDeclarations.get(e);const[o,t]=i(e);return"exclude"===o?null:o&&"keep"!==o?"replace"===o?t:void 0:e},_.hasBuildDeclaration=e=>{if(_.builtDeclarations.has(e))return void 0!==_.builtDeclarations.get(e);const[o]=i(e);return!!o&&"mock"!==o};const a=e=>_.hasBuildDeclaration(e),l=e=>_.getBuildDeclaration(e);_.isExcludedDef=e=>{const o=_.getResolution(e);return(!o||"exclude"===o)&&a(e)&&null===l(e)},_.isProvidedDef=e=>a(e)&&null!==l(e),_.getDefaults().set("@StoreDevtoolsModule",["exclude"]),_.indexValue=0,_.index=()=>_.indexValue++;const p=_},589:(e,o,t)=>{t.d(o,{A:()=>n});var r=t(20);const n=(e,o,t,n)=>{var s;const c=null!==(s=(0,r.A)(e,o))&&void 0!==s?s:{};if(!n&&c.set&&c.set.__ngMocksProxy)return c.set(t),t;const _={configurable:!0,enumerable:!0};return"get"===n&&c.set?_.set=c.set:"set"===n&&c.get&&(_.get=c.get),n?_[n]=t:(_.writable=!0,_.value=t),Object.defineProperty(e,o,_),t}},195:(e,o,t)=>{t.d(o,{A:()=>_});var r=t(331),n=t(794),s=t(20),c=t(932);const _=(e,o,t)=>{if("string"==typeof o)return c.A.mock(e,o,t);let _=e,i=o;const a=["__zone_symbol__unconfigurables"];"function"==typeof o&&(_=c.A.createClone(o),i=e,a.push(...Object.getOwnPropertyNames(_)));const l=[...(0,r.A)(i),...(0,n.A)(i)];for(const e of l){const o=-1===a.indexOf(e)?(0,s.A)(i,e):void 0;o&&Object.prototype.hasOwnProperty.call(o,"value")&&void 0===o.value||c.A.definePropertyDescriptor(_,e,o)}return _}},760:(e,o,t)=>{t.d(o,{A:()=>s});var r=t(73),n=t(589);const s=e=>{const o=[],t=r.A.configInstance.get(e);if(null==t?void 0:t.overloads)for(const[e,r,s]of t.overloads)e?o.push((o=>{(0,n.A)(o,e,r,s)})):o.push(r);return o}},365:(e,o,t)=>{t.d(o,{A:()=>_});var r=t(6),n=t(331),s=t(794),c=t(20);const _=(e,o,t,_)=>{const i=function(...r){return(_||e).apply(o===this?t:this,r)};for(const o of[...(0,n.A)(e),...(0,s.A)(e)]){const t=(0,c.A)(e,o);(0,r.A)(i,o,t)}return i}},6:(e,o,t)=>{t.d(o,{A:()=>n});var r=t(20);const n=(e,o,t)=>{if(!t||!e)return!1;if(Object.defineProperty){const n=(0,r.A)(e,o);if(!1===(null==n?void 0:n.configurable))return!1;Object.defineProperty(e,o,Object.assign(Object.assign(Object.assign({},t),{configurable:!0}),void 0===t.get&&void 0===t.set||!1===t.writable?{writable:!0}:{}))}else e[o]=t.value;return!0}},331:(e,o,t)=>{t.d(o,{A:()=>_});var r=t(970);const n=["sanitize","bypassSecurityTrustHtml","bypassSecurityTrustStyle","bypassSecurityTrustScript","bypassSecurityTrustUrl","bypassSecurityTrustResourceUrl"],s={DomSanitizer:n,Sanitizer:n},c=e=>{var o;const t=Object.getOwnPropertyNames(e);for(const n of null!==(o=s[(0,r.A)(e)])&&void 0!==o?o:[])t.push(n);return t},_=e=>{const o=[];let t=e;for(;t&&null!==Object.getPrototypeOf(t);){for(const e of c(t)){if("constructor"===e)continue;const r=Object.getOwnPropertyDescriptor(t,e);r&&(r.get||r.set)||-1!==o.indexOf(e)||o.push(e)}t=Object.getPrototypeOf(t)}return o}},794:(e,o,t)=>{t.d(o,{A:()=>r});const r=e=>{const o=[];let t=e;for(;t&&null!==Object.getPrototypeOf(t);){for(const e of Object.getOwnPropertyNames(t)){if("constructor"===e)continue;const r=Object.getOwnPropertyDescriptor(t,e);r&&(r.get||r.set)&&-1===o.indexOf(e)&&o.push(e)}t=Object.getPrototypeOf(t)}return o}},20:(e,o,t)=>{t.d(o,{A:()=>r});const r=(e,o)=>{let t=e;for(;t&&null!==Object.getPrototypeOf(t);){const e=Object.getOwnPropertyDescriptor(t,o);if(e)return e;t=Object.getPrototypeOf(t)}}},932:(e,o,t)=>{t.d(o,{A:()=>A,O:()=>M});var r=t(102),n=t(365),s=t(174),c=t(970),_=t(6),i=t(331),a=t(794),l=t(20);const p=(e,o=!1)=>{const t=p.customMockFunction&&!o?p.customMockFunction(e):e=>(n&&n(e),r);let r,n;return(0,s.A)(t,"__ngMocks",!0),(0,s.A)(t,"__ngMocksSet",(e=>n=e)),(0,s.A)(t,"__ngMocksGet",(e=>r=e)),t},u=p;var d=t(763),f=t(218),g=t(73);const k=["canActivate","canActivateChild","canDeactivate","canMatch","canLoad"],m=(e,o)=>Array.isArray(e[o])?(e=>{const o=[];for(const t of e)!g.A.isProvidedDef(t)&&g.A.isExcludedDef(d.ZG)||(o.push(t),(0,f.p)(t)||g.A.touches.add(t));return o})(e[o]):e[o],h=(e,o)=>{if(g.A.cacheDeclarations.has(e))return g.A.cacheDeclarations.get(e);if("object"!=typeof e)return e;if(o.has(e))return e;let t,r=!1;return Array.isArray(e)?[r,t]=((e,o,t)=>{const r=[];let n=!1;e.set(o,r);for(const s of o)g.A.isExcludedDef(s)?n=n||!0:(r.push(t(s,e)),n=n||r[r.length-1]!==s);return[n,r]})(o,e,h):e&&([r,t]=((e,o,t)=>{let r={},n=!1;e.set(o,r);for(const s of Object.keys(o))g.A.isExcludedDef(o[s])?n=n||!0:(r[s]=t(o[s],e),n=n||r[s]!==o[s]);for(const e of k){const o=m(r,e);o&&r[e].length!==o.length&&(n=n||!0,r=Object.assign(Object.assign({},r),{[e]:o}))}if("object"==typeof r.resolve&&r.resolve){const e={};let o=!1;for(const t of Object.keys(r.resolve)){const n=r.resolve[t];g.A.isProvidedDef(n)||!g.A.isExcludedDef(d.C3)?(e[t]=n,(0,f.p)(n)||g.A.touches.add(n)):o=o||!0}o&&(n=n||!0,r=Object.assign(Object.assign({},r),{resolve:e}))}return[n,r]})(o,e,h)),r?(Object.setPrototypeOf(t,Object.getPrototypeOf(e)),t):e};var v=t(663),b=t(465);(0,r.A)().ngMockshelperMockService=(0,r.A)().ngMockshelperMockService||{mockFunction:u,registerMockFunction:e=>{(0,r.A)().ngMockshelperMockService.mockFunction.customMockFunction=e},createClone:n.A,createMockFromPrototype:e=>{const o=(0,c.A)(e),t={};(0,s.A)(t,"__ngMocks",!0);const r=A.extractMethodsFromPrototype(e);for(const e of r)A.mock(t,e,o);const n=A.extractPropertiesFromPrototype(e);for(const e of n)A.mock(t,e,"get",o),A.mock(t,e,"set",o);return Object.setPrototypeOf(t,e),t},definePropertyDescriptor:_.A,extractMethodsFromPrototype:i.A,extractPropertiesFromPrototype:a.A,extractPropertyDescriptor:l.A,mock:(e,o,...t)=>{const{accessType:r,mockName:n}=(e=>{let o,t;return e.length>0&&"get"!==e[0]&&"set"!==e[0]?t=e[0]:e.length>0&&("get"===e[0]||"set"===e[0])&&(o=e[0],t=e[1]),{accessType:o,mockName:t}})(t),s=Object.getOwnPropertyDescriptor(e,o);if(s&&s[r||"value"])return s[r||"value"];const _=((e,o,t,r)=>`${null!=o?o:"function"==typeof t.prototype?t.prototype.name:(0,c.A)(t)}.${e}${null!=r?r:""}`)(o,n,e,r),i=A.mockFunction(_,!!r),a=((e,o,t)=>Object.assign(Object.assign(Object.assign(Object.assign({},"get"===t&&e&&e.set?{set:e.set}:{}),"set"===t&&e&&e.get?{get:e.get}:{}),t?{}:{writable:!0}),{[t||"value"]:o,configurable:!0,enumerable:!0}))(s,i,r);return a.get&&a.set&&a.get.__ngMocks&&a.set.__ngMocks&&a.set.__ngMocksSet((e=>a.get.__ngMocksGet(e))),Object.defineProperty(e,o,a),i},replaceWithMocks:e=>{const o=new Map,t=h(e,o);return o.clear(),t},resolveProvider:v.A,useFactory:b.A};const A=(0,r.A)().ngMockshelperMockService;function M(e){(0,r.A)().ngMockshelperMockService.registerMockFunction(e)}},663:(e,o,t)=>{t.d(o,{A:()=>u});var r=t(456),n=t(763),s=t(285),c=t(297),_=t(152),i=t(73),a=t(932),l=t(415);const p=(e,o,t)=>{let r=!1,n=!e;return o&&e&&!n&&(n=((e,o,...t)=>{for(const r of t)if(e[r]!==o[r])return!0;return!1})(o,e,"provide","useValue","useClass","useExisting","useFactory","deps")),(o===t&&e!==o||o!==t&&n)&&(r=!0),!r},u=(e,o,t)=>{const{provide:u,multi:d,change:f}=((e,o)=>{const t=(0,c.A)(e);return{change:()=>{o&&o()},multi:e!==t&&!!e.multi,provide:t}})(e,t);if(((e,o)=>null===i.A.builtProviders.get(o)||(o!==e&&e.deps&&(0,r.by)(e.deps,i.A.config.get("ngMocksDeps")),((e,o)=>{if(((e,o)=>i.A.builtProviders.has(n.rO)&&null===i.A.builtProviders.get(n.rO)&&(0,_.S)(o)&&"InjectionToken HTTP_INTERCEPTORS"===o.toString()&&o!==e)(e,o)){if(e.useFactory||e.useValue)return!0;const o=(0,s.A)(e.useExisting)||e.useClass;if(!i.A.builtProviders.has(o)||null===i.A.builtProviders.get(o))return!0}return!1})(e,o)))(e,u))return f();if(((e,o)=>{var t;return!(!e||"object"!=typeof e||!e.useExisting||!e.useExisting.mockOf&&(i.A.getResolution(o)&&!(null===(t=i.A.config.get(o))||void 0===t?void 0:t.__internal)||"keep"!==i.A.getResolution((0,s.A)(e.useExisting))))})(e,u))return i.A.touches.add(u),e;if(o.has(u))return((e,o)=>{let t=o;const r=i.A.builtProviders.get(e);return r&&(t=r),"function"==typeof t&&(t={provide:e,useClass:t}),t})(u,o.get(u));const g=((e,o,t)=>{var r;let n=((e,o)=>{if(i.A.builtProviders.has(o)){const t=i.A.builtProviders.get(o);return t===o?e:t}})(e,o);return!n&&i.A.flags.has("skipMock")&&"mock"!==i.A.getResolution(o)&&(null===(r=i.A.config.get("ngMocksDepsSkip"))||void 0===r||r.add(o),n=e),n||(n=(0,l.A)(e)),n=((e,o,t)=>{if(o!==e&&t&&t.useValue){const e=a.A.replaceWithMocks(t.useValue);return e===t.useValue?t:Object.assign(Object.assign({},t),{useValue:e})}return t})(e,o,n),p(n,e,o)||t(),n&&i.A.touches.add(o),n})(e,u,f);return d&&"object"==typeof g?Object.assign(Object.assign({},g),{multi:d}):g}},465:(e,o,t)=>{t.d(o,{A:()=>p});var r=t(614),n=t(456),s=t(152),c=t(73),_=t(195),i=t(760),a=t(839);const l=e=>(0,s.S)(e)||"string"==typeof e,p=(e,o,t)=>({deps:[r.Injector],provide:e,useFactory:r=>{const s=o?o():(0,a.K)(e),p=c.A.getOverrides().get(e),u=p?(0,n.LG)(p):[];return t&&u.push(t),u.push(...(0,i.A)(e)),((e,o,t,r,n)=>{let s=o;for(const o of t){const t=o(s,r);l(e)?s=t:o!==n?t&&(s=(0,_.A)(s,t)):s=t}return s})(e,s,u,r,t)}})},415:(e,o,t)=>{t.d(o,{A:()=>g});var r=t(295),n=t(297),s=t(152),c=t(73),_=t(6),i=t(20),a=t(465),l=t(839);const{neverMockProvidedFunction:p,neverMockToken:u}=r.A,d=[[e=>"boolean"==typeof e,!1],[e=>"number"==typeof e,0],[e=>"string"==typeof e,""],[e=>null===e,null]],f=(e,o,t)=>{var r;if(o===e)return t?(0,a.A)(e,(()=>{})):void 0;if(e.multi)return void(null===(r=c.A.config.get("ngMocksMulti"))||void 0===r||r.add(o));let n;return-1!==Object.keys(e).indexOf("useValue")?n=((e,o)=>(0,a.A)(o,(()=>e.useValue&&"object"==typeof e.useValue?(0,l.K)(e.useValue):(e=>{for(const[o,t]of d)if(o(e))return t})(e.useValue))))(e,o):-1!==Object.keys(e).indexOf("useExisting")?n=e:-1!==Object.keys(e).indexOf("useClass")?n=((e,o)=>c.A.builtProviders.has(e.useClass)&&c.A.builtProviders.get(e.useClass)===e.useClass?e:(0,a.A)(o,(()=>(0,l.K)(e.useClass))))(e,o):-1!==Object.keys(e).indexOf("useFactory")&&(n=(0,a.A)(o,(()=>({})))),n},g=(e,o=!1)=>{const t=(0,n.A)(e);if("mock"===c.A.getResolution(t));else{if((e=>"function"==typeof e&&-1!==p.indexOf(e.name))(t))return e;if((e=>(0,s.S)(e)&&-1!==u.indexOf(e.toString()))(t))return}const r=c.A.flags.has("cacheProvider")?c.A.cacheProviders:void 0;return t===e&&r&&r.has(t)?r.get(t):((e,o,t)=>{let r;return"function"==typeof o&&(r=((e,o)=>(0,a.A)(o,(()=>{const t=(0,l.K)(o);return o!==e&&-1!==Object.keys(e).indexOf("useClass")&&((e,o)=>{const t=Object.getOwnPropertyNames(e),r=(0,l.K)(o);for(const o of Object.getOwnPropertyNames(r)){if(-1!==t.indexOf(o))continue;const n=(0,i.A)(r,o);(0,_.A)(e,o,n)}})(t,e.useClass),t})))(e,o)),o===e&&r&&t&&t.set(o,r),r})(e,t,r)||f(e,t,o)}},839:(e,o,t)=>{t.d(o,{K:()=>a});var r=t(970),n=t(195);const s=e=>{if("function"!=typeof e)return!1;if(!e.prototype)return!0;if((e=>!!(e.ɵprov||e.__annotations__||e.__parameters__||e.parameters))(e))return!1;const o=e.toString();if(null!==o.match(/^class\b/))return!1;const t=o.match(/^function\s+([^\s(]+)\(/);return null===t||!((e,o,t)=>{if(null!==e.match(/^class/))return!0;if(Object.keys(t.prototype).length>0)return!0;const r=e.codePointAt(0);if(r&&r>=65&&r<=90&&null!==o.match(/\bthis\./gm))return!0;const n=new RegExp(`\\(this,\\s*${e}\\)`,"mg");return null!==o.match(n)})(t[1],o,e)};var c=t(932);const _=[[e=>"function"==typeof e&&!s(e),(e,o)=>{const t=c.A.createMockFromPrototype(o.prototype);return e.set(o,t),t}],[s,(e,o,t)=>{const n=c.A.mockFunction(`func:${t||(0,r.A)(o)}`);return e.set(o,n()),n}],[e=>Array.isArray(e),()=>[]],[e=>null!==e&&"object"==typeof e&&"InjectionToken"!==e.ngMetadataName&&"object"==typeof Object.getPrototypeOf(e),(e,o,t,r)=>{const n=c.A.createMockFromPrototype(o.constructor.prototype);e.set(o,n);for(const s of Object.keys(o)){const c=r(e,o[s],`${t||"instance"}.${s}`);void 0!==c&&(n[s]=c)}return Object.setPrototypeOf(n,Object.getPrototypeOf(o)),n}]],i=(e,o,t="",r)=>{const s=((e,o,t,r)=>{var n;for(const[s,c]of _)if(s(o))return null!==(n=e.get(o))&&void 0!==n?n:c(e,o,t,r)})(e,o,t,i);return r&&(0,n.A)(s,r),s};function a(e,...o){const t=o.length>0&&"string"==typeof o[0]?o[0]:o[1],r=o.length>0&&o[0]&&"object"==typeof o[0]?o[0]:void 0,n=new Map,s=i(n,e,t,r);return n.clear(),s}},673:(e,o,t)=>{t.d(o,{A:()=>x});var r=t(614),n=t(174),s=t(439),c=t(184);const _=(e,o)=>{const t=e.indexOf(o);-1!==t&&e.splice(t,1),"Injectable"!==o&&"Pipe"!==o&&"Directive"!==o&&"Component"!==o&&"NgModule"!==o||e.push(o)},i=e=>{const o=[];for(const t of Object.keys(e))o.push(t);return o},a=e=>(o,t,r,n)=>{var _;const{alias:i,required:a}=(0,c.A)({name:t,alias:null!==(_=r.alias)&&void 0!==_?_:r.bindingPropertyName,required:r.required}),l=(0,s.A)({name:t,alias:i,required:a});let p=!0;for(const o of n[e]){if(o===l){p=!1;break}const{name:e,alias:r,required:n}=(0,c.A)(o);if(e===t&&r===i&&n===a){p=!1;break}}p&&n[e].unshift(l)},l=a("inputs"),p=a("outputs"),u=e=>(o,t,r,n)=>{n.queries[t]||(n.queries[t]=Object.assign(Object.assign({isViewQuery:e,ngMetadataName:o,selector:r.selector},void 0===r.read?{}:{read:r.read}),void 0===r.static?{}:{static:r.static}))},d=u(!1),f=u(!0),g=e=>(o,t,r,n)=>{n.queries[t]||(n.queries[t]=Object.assign(Object.assign(Object.assign({isViewQuery:e,ngMetadataName:o,selector:r.selector},void 0===r.descendants?{}:{descendants:r.descendants}),void 0===r.emitDistinctChangesOnly?{}:{emitDistinctChangesOnly:r.emitDistinctChangesOnly}),void 0===r.read?{}:{read:r.read}))},k={ContentChild:d,ContentChildren:g(!1),HostBinding:(e,o,t,r)=>{const n=`[${t.hostPropertyName||o}]`;r.host[n]||(r.host[n]=o),r.hostBindings.push([o,t.hostPropertyName||o,...t.args?[t.args]:[]])},HostListener:(e,o,t,r)=>{const n=`(${t.eventName||o})`;r.host[n]||(r.host[n]=`${o}($event)`),r.hostListeners.push([o,t.eventName||o,...t.args?[t.args]:[]])},Input:l,Output:p,ViewChild:f,ViewChildren:g(!0)},m=e=>{const o=a(e);return(e,t,r,n)=>{var s;const{alias:c,required:_}=void 0===(null===(s=r.args)||void 0===s?void 0:s[0])?{}:"string"==typeof r.args[0]?{alias:r.args[0]}:r.args[0];o(e,t,{alias:c,required:_,bindingPropertyName:c},n)}},h=m("inputs"),v=m("outputs"),b=e=>(o,t,r,n)=>{n.queries[t]||(n.queries[t]=Object.assign({isViewQuery:e,ngMetadataName:o,selector:r.args[0]},r.args[1]))},A=b(!1),M=b(!0),y={ContentChild:A,ContentChildren:A,HostBinding:(e,o,t,r)=>{var n;const s=`[${(null===(n=t.args)||void 0===n?void 0:n[0])||o}]`;r.host[s]||(r.host[s]=o),r.hostBindings.push([o,...t.args||[]])},HostListener:(e,o,t,r)=>{var n;const s=`(${(null===(n=t.args)||void 0===n?void 0:n[0])||o})`;r.host[s]||(r.host[s]=`${o}($event)`),r.hostListeners.push([o,...t.args||[]])},Input:h,Output:v,ViewChild:M,ViewChildren:M},w=(e,o)=>{if(e){e.inputs=e.inputs||[];for(const t of o.inputs)-1===e.inputs.indexOf(t)&&e.inputs.push(t);e.outputs=e.outputs||[];for(const t of o.outputs)-1===e.outputs.indexOf(t)&&e.outputs.push(t);e.queries=Object.assign(Object.assign({},e.queries||[]),o.queries),e.hostBindings=o.hostBindings,e.hostListeners=o.hostListeners}},O=new r["ɵReflectionCapabilities"],C=e=>{if("function"!=typeof e&&"object"!=typeof e)return{};if(Object.prototype.hasOwnProperty.call(e,"__ngMocksParsed"))return e.__ngMocksDeclarations;const o=Object.getPrototypeOf(e),t=o?C(o):{},r=(e=>({host:e.host?Object.assign({},e.host):{},hostBindings:e.hostBindings?[...e.hostBindings]:[],hostListeners:e.hostListeners?[...e.hostListeners]:[],attributes:e.attributes?[...e.attributes]:[],inputs:e.inputs?[...e.inputs]:[],outputs:e.outputs?[...e.outputs]:[],propDecorators:e.propDecorators?Object.assign({},e.propDecorators):{},queries:e.queries?Object.assign({},e.queries):{},decorators:e.decorators?[...e.decorators]:[]}))(t);return(0,n.A)(e,"__ngMocksParsed",!0),((e,o)=>{if(Object.prototype.hasOwnProperty.call(e,"__parameters__")&&e.__parameters__)for(const t of e.__parameters__)for(const e of t||[])"Attribute"===e.ngMetadataName&&-1===o.attributes.indexOf(e.attributeName)&&o.attributes.push(e.attributeName)})(e,r),((e,o)=>{if(Object.prototype.hasOwnProperty.call(e,"__annotations__")&&e.__annotations__)for(const t of e.__annotations__){const e=null==t?void 0:t.ngMetadataName;e&&(o[e]=Object.assign(Object.assign({},t),{attributes:o.attributes}),_(o.decorators,e))}})(e,r),((e,o)=>{var t,r;if(Object.prototype.hasOwnProperty.call(e,"decorators")&&e.decorators)for(const n of e.decorators){const e=null===(r=null===(t=null==n?void 0:n.type)||void 0===t?void 0:t.prototype)||void 0===r?void 0:r.ngMetadataName;e&&(o[e]=n.args?Object.assign({},n.args[0]):{},_(o.decorators,e))}})(e,r),((e,o)=>{var t,r,n;if(Object.prototype.hasOwnProperty.call(e,"propDecorators")&&e.propDecorators)for(const s of i(e.propDecorators)){o.propDecorators[s]=[...o.propDecorators[s]||[],...e.propDecorators[s]];for(const c of e.propDecorators[s]){const e=null===(r=null===(t=null==c?void 0:c.type)||void 0===t?void 0:t.prototype)||void 0===r?void 0:r.ngMetadataName;e&&(null===(n=y[e])||void 0===n||n.call(y,e,s,c,o))}}})(e,r),((e,o)=>{var t;if(Object.prototype.hasOwnProperty.call(e,"__prop__metadata__")&&e.__prop__metadata__)for(const r of i(e.__prop__metadata__)){const n=e.__prop__metadata__[r];for(const e of n){const n=null==e?void 0:e.ngMetadataName;n&&(null===(t=k[n])||void 0===t||t.call(k,n,r,e,o))}}})(e,r),w(r.Directive,r),w(r.Component,r),(0,n.A)(e,"__ngMocksDeclarations",Object.assign(Object.assign(Object.assign({},t),r),{parameters:O.parameters(e)})),e.__ngMocksDeclarations},x=C},614:(e,o,t)=>{e.exports=(e=>{var o={};return t.d(o,e),o})({ChangeDetectorRef:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ChangeDetectorRef,Component:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Component,ComponentFactoryResolver:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ComponentFactoryResolver,ContentChild:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ContentChild,ContentChildren:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ContentChildren,DebugElement:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.DebugElement,Directive:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Directive,ElementRef:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ElementRef,EventEmitter:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.EventEmitter,InjectionToken:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.InjectionToken,Injector:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Injector,Input:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Input,NgModule:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.NgModule,Optional:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Optional,Output:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Output,Pipe:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Pipe,QueryList:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.QueryList,Self:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Self,TemplateRef:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.TemplateRef,ViewChild:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ViewChild,ViewChildren:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ViewChildren,ViewContainerRef:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ViewContainerRef,ɵReflectionCapabilities:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__["ɵReflectionCapabilities"]})},957:(e,o,t)=>{e.exports=(e=>{var o={};return t.d(o,e),o})({TestBed:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_testing_89899de6__.TestBed,getTestBed:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_testing_89899de6__.getTestBed})}},__webpack_module_cache__={};function __webpack_require__(e){var o=__webpack_module_cache__[e];if(void 0!==o)return o.exports;var t=__webpack_module_cache__[e]={exports:{}};return __webpack_modules__[e](t,t.exports,__webpack_require__),t.exports}__webpack_require__.d=(e,o)=>{for(var t in o)__webpack_require__.o(o,t)&&!__webpack_require__.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.d(__webpack_exports__,{rX:()=>types_namespaceObject.IMockBuilder,HA:()=>types_namespaceObject.IMockBuilderConfig,Wn:()=>types_namespaceObject.IMockBuilderConfigAll,Me:()=>types_namespaceObject.IMockBuilderConfigComponent,I7:()=>types_namespaceObject.IMockBuilderConfigDirective,tv:()=>types_namespaceObject.IMockBuilderConfigModule,OG:()=>types_namespaceObject.IMockBuilderExtended,sF:()=>types_namespaceObject.IMockBuilderProvider,me:()=>types_namespaceObject.IMockBuilderResult,bk:()=>LegacyControlValueAccessor,JT:()=>Mock,_V:()=>MockBuilder,Am:()=>MockComponent,$u:()=>MockComponents,D2:()=>mock_control_value_accessor_namespaceObject.MockControlValueAccessor,dw:()=>MockDeclaration,uV:()=>MockDeclarations,Cc:()=>MockDirective,nr:()=>MockDirectives,Wm:()=>MockInstance,BN:()=>MockModule,ZJ:()=>MockPipe,yI:()=>MockPipes,Qo:()=>MockProvider,dD:()=>MockProviders,Ty:()=>MockRender,P0:()=>MockRenderFactory,mB:()=>MockReset,KH:()=>mock_service.K,b6:()=>mock_control_value_accessor_namespaceObject.MockValidator,Je:()=>mock_component_types_namespaceObject.MockedComponent,T4:()=>mock_directive_types_namespaceObject.MockedDirective,qZ:()=>mock_module_types_namespaceObject.MockedModule,cm:()=>mock_pipe_types_namespaceObject.MockedPipe,en:()=>core_tokens.en,ZG:()=>core_tokens.ZG,rO:()=>core_tokens.rO,So:()=>core_tokens.So,C3:()=>core_tokens.C3,gG:()=>core_tokens.gG,Em:()=>core_tokens.Em,Ah:()=>core_helpers.Ah,xz:()=>getMockedNgDefOf,VK:()=>getSourceOfMock,d5:()=>core_helpers.d5,IA:()=>isMockControlValueAccessor,Bt:()=>isMockNgDef,AW:()=>isMockOf,lt:()=>isMockValidator,Fk:()=>isMockedNgDefOf,pA:()=>func_is_ng_def.p,SM:()=>func_is_ng_injection_token.S,H5:()=>ngMocks});var mock_control_value_accessor_namespaceObject={};__webpack_require__.r(mock_control_value_accessor_namespaceObject),__webpack_require__.d(mock_control_value_accessor_namespaceObject,{b:()=>LegacyControlValueAccessor});var types_namespaceObject={};__webpack_require__.r(types_namespaceObject);var mock_module_types_namespaceObject={};__webpack_require__.r(mock_module_types_namespaceObject);var mock_component_types_namespaceObject={};__webpack_require__.r(mock_component_types_namespaceObject);var mock_directive_types_namespaceObject={};__webpack_require__.r(mock_directive_types_namespaceObject);var mock_pipe_types_namespaceObject={};__webpack_require__.r(mock_pipe_types_namespaceObject);var ng_mocks_universe=__webpack_require__(73),_a,_b;const stackRoot={id:{},level:"root"},stack=ng_mocks_universe.A.global.get("reporter-stack")||[Object.assign({},stackRoot)];ng_mocks_universe.A.global.set("reporter-stack",stack);const current=()=>stack[stack.length-1],listenersPush=null!==(_a=ng_mocks_universe.A.global.get("reporter-stack-push"))&&void 0!==_a?_a:[];ng_mocks_universe.A.global.set("reporter-stack-push",listenersPush);const listenersPop=null!==(_b=ng_mocks_universe.A.global.get("reporter-stack-pop"))&&void 0!==_b?_b:[];ng_mocks_universe.A.global.set("reporter-stack-pop",listenersPop);const stackPush=()=>{const e={};ng_mocks_universe.A.global.set("reporter-stack-id",e);const o={id:e,level:"runtime"};stack.push(o);for(const e of listenersPush)e(o,stack)},stackPop=()=>{const e=stack.pop();if(0===stack.length&&stack.push("root"===(null==e?void 0:e.level)?e:Object.assign({},stackRoot)),e&&"root"!==e.level)for(const o of listenersPop)o(e,stack);ng_mocks_universe.A.global.set("reporter-stack-id",stack[stack.length-1].id)},subscribePush=e=>{listenersPush.indexOf(e)&&listenersPush.push(e),stack.length>0&&e(stack[stack.length-1],stack)},subscribePop=e=>{-1===listenersPop.indexOf(e)&&listenersPop.push(e)},unsubscribePush=e=>{const o=listenersPush.indexOf(e);-1!==o&&listenersPush.splice(o,1)},unsubscribePop=e=>{const o=listenersPop.indexOf(e);-1!==o&&listenersPop.splice(o,1)},ng_mocks_stack={current,stackPop,stackPush,subscribePop,subscribePush,unsubscribePop,unsubscribePush};var core_=__webpack_require__(614),testing_=__webpack_require__(957),core_helpers=__webpack_require__(456),core_tokens=__webpack_require__(763);const func_extract_tokens=e=>{let o,t,r;for(const n of(0,core_helpers.Bq)(e||[]))"object"==typeof n&&(n.provide===core_tokens.en&&(o=n.useValue),n.provide===core_tokens.So&&(t=n.useValue),n.provide===core_tokens.Em&&(r=n.useValue));return{mocks:o,overrides:t,touches:r}};var core_define_property=__webpack_require__(174),collect_declarations=__webpack_require__(673),func_is_ng_injection_token=__webpack_require__(152);const getNgType=e=>{if("string"==typeof e)return;if((0,func_is_ng_injection_token.S)(e))return"Injectable";const{decorators:o}=(0,collect_declarations.A)(e);for(let e=o.length-1;e>=0;e-=1)if("Injectable"!==o[e])return o[e];return o.length>0?"Injectable":void 0};function isStandalone(e){const o=getNgType(e);return!(!o||"Injectable"===o)&&!0===(0,collect_declarations.A)(e)[o].standalone}var helper_extract_property_descriptor=__webpack_require__(20);const core_def_stack=class{constructor(){this.stack=[],this.push()}push(){this.stack.push(new Map)}pop(){var e;return null!==(e=this.stack.pop())&&void 0!==e?e:new Map}has(e){for(let o=this.stack.length-1;o>=0;o-=1)if(this.stack[o].has(e))return!0;return!1}get(e){for(let o=this.stack.length-1;o>=0;o-=1)if(this.stack[o].has(e))return this.stack[o].get(e)}set(e,o){for(let t=this.stack.length-1;t>=0;t-=1)this.stack[t].set(e,o);return this}merge(e){for(const[o,t]of(0,core_helpers.Nn)(e))this.set(o,t);return this}};var func_get_name=__webpack_require__(970),func_is_ng_def=__webpack_require__(218),func_is_ng_module_def_with_providers=__webpack_require__(659),core_config=__webpack_require__(295);class MockBuilderStash{constructor(){this.data={}}backup(){this.data={builtDeclarations:ng_mocks_universe.A.builtDeclarations,builtProviders:ng_mocks_universe.A.builtProviders,cacheDeclarations:ng_mocks_universe.A.cacheDeclarations,cacheProviders:ng_mocks_universe.A.cacheProviders,config:ng_mocks_universe.A.config,configInstance:ng_mocks_universe.A.configInstance,flags:ng_mocks_universe.A.flags,touches:ng_mocks_universe.A.touches},ng_mocks_universe.A.builtDeclarations=new Map,ng_mocks_universe.A.builtProviders=new Map,ng_mocks_universe.A.cacheDeclarations=new Map,ng_mocks_universe.A.cacheProviders=new Map,ng_mocks_universe.A.config=new Map,ng_mocks_universe.A.configInstance=new Map,ng_mocks_universe.A.flags=new Set(core_config.A.flags),ng_mocks_universe.A.touches=new Set}restore(){for(const e of Object.keys(this.data))ng_mocks_universe.A[e]=this.data[e]}}const core_reflect_provided_in=e=>{var o,t,r;if(e&&("object"==typeof e||"function"==typeof e))return null!==(t=null===(o=e.ɵprov)||void 0===o?void 0:o.providedIn)&&void 0!==t?t:null===(r=e.ngInjectableDef)||void 0===r?void 0:r.providedIn};var func_get_type=__webpack_require__(297),helper_resolve_provider=__webpack_require__(663);const add_requested_providers=(e,{providerDef:o,mockDef:t},r)=>{for(const t of(0,core_helpers.LG)(o))e.providers.push(t);for(const o of(0,core_helpers.Bq)(e.providers)){const e=(0,func_get_type.A)(o);ng_mocks_universe.A.touches.add(e),e!==o&&o.deps&&(0,core_helpers.by)(o.deps,ng_mocks_universe.A.config.get("ngMocksDeps"))}for(const o of(0,core_helpers.LG)(t))ng_mocks_universe.A.touches.has(o)||"root"!==core_reflect_provided_in(o)||(e.providers.push((0,helper_resolve_provider.A)(o,r)),ng_mocks_universe.A.touches.add(o))},apply_platform_modules=()=>{const e=(0,testing_.getTestBed)();if(e.ngModule)for(const o of(0,core_helpers.Bq)(e.ngModule))ng_mocks_universe.A.touches.add((0,func_get_type.A)(o))},error_empty_def=e=>{if(!e)throw new Error(["undefined / null has been passed into ng-mocks as a declaration / provider.","Please ensure that the current test file has correct imports:","imported files exist and imported declarations have been exported in the file."].join(" "))},func_is_jest_mock=e=>!(!e||"function"!=typeof e&&"object"!=typeof e||!(e._isMockFunction&&e.mockName&&e.__annotations__)),error_jest_mock=e=>{if(func_is_jest_mock(e))throw new Error([`ng-mocks got ${(0,func_get_name.A)(e)} which has been already mocked by jest.mock().`,"It is not possible to produce correct mocks for it, because jest.mock() removes Angular decorators.",`To fix this, please avoid jest.mock() on the file which exports ${(0,func_get_name.A)(e)} or add jest.dontMock() on it.`,"The same should be done for all related dependencies."].join(" "))},error_missing_decorators=e=>{throw new Error([`${(0,func_get_name.A)(e)} declaration has been passed into ng-mocks without Angular decorators.`,"Therefore, it cannot be properly handled.","Highly likely,","undefined"==typeof jest?"":"jest.mock() has been used on its file, or","ng-mocks is imported in production code, or got a class without Angular decoration.","Otherwise, please create an issue on github: https://github.com/help-me-mom/ng-mocks/issues/new?title=False%20positive%20ng-mocks%20not%20in%20JIT.","Thank you in advance for support."].join(" "))},core_reflect_body_catch=e=>o=>{error_empty_def(o);try{return e(o)}catch(e){error_jest_mock(o),error_missing_decorators(o)}},core_reflect_directive_resolve=e=>core_reflect_body_catch((e=>{const o=(0,collect_declarations.A)(e);if(o.Component)return o.Component;if(o.Directive)return o.Directive;throw new Error("Cannot resolve declarations")}))(e),core_reflect_module_resolve=e=>core_reflect_body_catch((e=>{const o=(0,collect_declarations.A)(e);if(o.NgModule)return o.NgModule;throw new Error("Cannot resolve declarations")}))(e),core_reflect_meta=e=>(0,func_is_ng_def.p)(e,"c")||(0,func_is_ng_def.p)(e,"d")?core_reflect_directive_resolve(e):(0,func_is_ng_def.p)(e,"m")?core_reflect_module_resolve(e):void 0;function getSourceOfMock(e){return"function"==typeof e&&e.mockOf?e.mockOf:e}const mark_exported=(e,o)=>{var t;const r=getSourceOfMock(e),n=null!==(t=ng_mocks_universe.A.configInstance.get(r))&&void 0!==t?t:{__set:!0};n.exported||(n.exported=new Set),o&&n.exported.add(getSourceOfMock(o)),n.__set&&(n.__set=void 0,ng_mocks_universe.A.configInstance.set(r,n))};var x=e=>{var o={};return __webpack_require__.d(o,e),o},y=e=>()=>e;const forms_namespaceObject=x({AbstractControl:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.AbstractControl,DefaultValueAccessor:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.DefaultValueAccessor,FormControl:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.FormControl,FormControlDirective:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.FormControlDirective,NG_ASYNC_VALIDATORS:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.NG_ASYNC_VALIDATORS,NG_VALIDATORS:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.NG_VALIDATORS,NG_VALUE_ACCESSOR:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.NG_VALUE_ACCESSOR,NgControl:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.NgControl,NgModel:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.NgModel}),AbstractControl=forms_namespaceObject.AbstractControl,DefaultValueAccessor=forms_namespaceObject.DefaultValueAccessor,FormControl=forms_namespaceObject.FormControl,FormControlDirective=forms_namespaceObject.FormControlDirective,NG_ASYNC_VALIDATORS=forms_namespaceObject.NG_ASYNC_VALIDATORS,NG_VALIDATORS=forms_namespaceObject.NG_VALIDATORS,NG_VALUE_ACCESSOR=forms_namespaceObject.NG_VALUE_ACCESSOR,NgControl=forms_namespaceObject.NgControl,NgModel=forms_namespaceObject.NgModel,core_form={AbstractControl,DefaultValueAccessor,FormControl,FormControlDirective,NG_ASYNC_VALIDATORS,NG_VALIDATORS,NG_VALUE_ACCESSOR,NgControl,NgModel},func_is_mock=e=>e&&"object"==typeof e&&!!e.__ngMocks;var mock_helper_stub=__webpack_require__(195),mock_instance_apply=__webpack_require__(760),helper_mock_service=__webpack_require__(932),func_directive_io_parse=__webpack_require__(184);const applyProxy=(e,o,t,r)=>{if(e.instance&&r&&(e.instance[r]=t),e.instance&&e.instance[o])return e.instance[o](t)};class MockControlValueAccessorProxy{constructor(e){this.target=e}registerOnChange(e){applyProxy(this,"registerOnChange",e,"__simulateChange")}registerOnTouched(e){applyProxy(this,"registerOnTouched",e,"__simulateTouch")}setDisabledState(e){applyProxy(this,"setDisabledState",e)}writeValue(e){applyProxy(this,"writeValue",e)}}class MockValidatorProxy{constructor(e){this.target=e}registerOnValidatorChange(e){applyProxy(this,"registerOnValidatorChange",e,"__simulateValidatorChange")}validate(e){return this.instance&&this.instance.validate?this.instance.validate(e):null}}class MockAsyncValidatorProxy{constructor(e){this.target=e}registerOnValidatorChange(e){applyProxy(this,"registerOnValidatorChange",e,"__simulateValidatorChange")}validate(e){if(this.instance&&this.instance.validate){const o=this.instance.validate(e);return void 0===o?Promise.resolve(null):o}return Promise.resolve(null)}}const setValueAccessor=(e,o)=>{if(o&&!o.valueAccessor&&e.__ngMocksConfig.setControlValueAccessor)try{o.valueAccessor=new MockControlValueAccessorProxy(e.__ngMocksCtor)}catch(e){}},installValueAccessor=(e,o)=>{e.valueAccessor.instance||e.valueAccessor.target!==o.__ngMocksCtor||(e.valueAccessor.instance=o,helper_mock_service.A.mock(o,"registerOnChange"),helper_mock_service.A.mock(o,"registerOnTouched"),helper_mock_service.A.mock(o,"setDisabledState"),helper_mock_service.A.mock(o,"writeValue"),o.__ngMocksConfig.isControlValueAccessor=!0)},installValidator=(e,o)=>{for(const t of e)t.instance||t.target!==o.__ngMocksCtor||(t.instance=o,helper_mock_service.A.mock(o,"registerOnValidatorChange"),helper_mock_service.A.mock(o,"validate"),o.__ngMocksConfig.isValidator=!0)},applyNgValueAccessor=(e,o)=>{setValueAccessor(e,o);try{o&&(installValueAccessor(o,e),installValidator(o._rawValidators,e),installValidator(o._rawAsyncValidators,e))}catch(e){}},applyOutputs=e=>{const o=[];for(const t of e.__ngMocksConfig.outputs||[])o.push((0,func_directive_io_parse.A)(t).name);for(const t of o)e[t]||Object.getOwnPropertyDescriptor(e,t)||(e[t]=new core_.EventEmitter)},applyPrototype=(e,o)=>{for(const t of[...helper_mock_service.A.extractMethodsFromPrototype(o),...helper_mock_service.A.extractPropertiesFromPrototype(o)]){const r=helper_mock_service.A.extractPropertyDescriptor(o,t);helper_mock_service.A.definePropertyDescriptor(e,t,r)}},applyMethods=(e,o)=>{for(const t of helper_mock_service.A.extractMethodsFromPrototype(o))e[t]||Object.getOwnPropertyDescriptor(e,t)||helper_mock_service.A.mock(e,t)},applyProps=(e,o)=>{for(const t of helper_mock_service.A.extractPropertiesFromPrototype(o))e[t]||Object.getOwnPropertyDescriptor(e,t)||(helper_mock_service.A.mock(e,t,"get"),helper_mock_service.A.mock(e,t,"set"))},applyOverrides=(e,o,t)=>{const r=ng_mocks_universe.A.getOverrides().get(o),n=r?(0,core_helpers.LG)(r):[];e.__ngMocksConfig.init&&n.push(e.__ngMocksConfig.init),n.push(...(0,mock_instance_apply.A)(o));for(const o of n){const r=o(e,t);r&&(0,mock_helper_stub.A)(e,r)}};class Mock{constructor(e=null,o=null){const t=this.constructor.mockOf;(0,core_define_property.A)(this,"__ngMocks",!0),(0,core_define_property.A)(this,"__ngMocksInjector",e),(0,core_define_property.A)(this,"__ngMocksCtor",this.constructor);for(const e of this.__ngMocksConfig.queryScanKeys||[])(0,core_define_property.A)(this,`__ngMocksVcr_${e}`,void 0);for(const e of this.__ngMocksConfig.hostBindings||[])helper_mock_service.A.mock(this,e,"get"),helper_mock_service.A.mock(this,e,"set");for(const e of this.__ngMocksConfig.hostListeners||[])helper_mock_service.A.mock(this,e);func_is_mock(this)&&(applyNgValueAccessor(this,o),applyOutputs(this),applyPrototype(this,Object.getPrototypeOf(this)),applyMethods(this,t.prototype),applyProps(this,t.prototype)),Object.setPrototypeOf(this,t.prototype),applyOverrides(this,t,null!=e?e:void 0)}}(0,core_define_property.A)(Mock,"parameters",[[core_.Injector,new core_.Optional],[core_form.NgControl||(()=>{}),new core_.Optional,new core_.Self]]);class LegacyControlValueAccessor extends Mock{__simulateChange(){}__simulateTouch(){}__simulateValidatorChange(){}}var func_directive_io_build=__webpack_require__(439);const decorate_inputs=(e,o,t)=>{if(o)for(const r of o){const{name:o,alias:n,required:s}=(0,func_directive_io_parse.A)(r);t&&-1!==t.indexOf(o)||(0,core_.Input)((0,func_directive_io_build.A)({name:o,alias:n,required:s},!0))(e.prototype,o)}},decorate_mock=(e,o,t={})=>{(0,core_define_property.A)(e,"mockOf",o),(0,core_define_property.A)(e,"nameConstructor",(0,func_get_name.A)(e)),(0,core_define_property.A)(e,"name",`MockOf${(0,func_get_name.A)(o)}`,!0);const r=ng_mocks_universe.A.getConfigMock().has(o)?Object.assign(Object.assign({},t),{config:Object.assign(Object.assign({},ng_mocks_universe.A.getConfigMock().get(o)),t.config)}):t;(0,core_define_property.A)(e.prototype,"__ngMocksConfig",r)},decorate_outputs=(e,o)=>{if(o)for(const t of o){const{name:o,alias:r,required:n}=(0,func_directive_io_parse.A)(t);(0,core_.Output)((0,func_directive_io_build.A)({name:o,alias:r,required:n},!0))(e.prototype,o)}},map={ContentChild:core_.ContentChild,ContentChildren:core_.ContentChildren,ViewChild:core_.ViewChild,ViewChildren:core_.ViewChildren},isInternalKey=e=>0===e.indexOf("__mock"),cloneVcrQuery=e=>Object.assign(Object.assign({},e),{ngMetadataName:e.ngMetadataName,read:core_.ViewContainerRef}),generateFinalQueries=e=>{const o=[],t=[];for(const r of Object.keys(e)){const n=e[r];o.push([r,n]),n.isViewQuery||isInternalKey(r)||(t.push(r),o.push([`__ngMocksVcr_${r}`,cloneVcrQuery(n)]))}return[o,t]},decorate_queries=(e,o)=>{if(!o)return[];const[t,r]=generateFinalQueries(o);for(const[o,r]of t)r.ngMetadataName&&(0,map[r.ngMetadataName])(r.selector,r)(e.prototype,o);return r};var func_extract_forward_ref=__webpack_require__(285);const to_existing_provider=(e,o)=>({provide:e,useExisting:o}),to_factory_provider=(e,o)=>({multi:!0,provide:e,useFactory:o}),processTokens=(e,o)=>{const t=(0,func_get_type.A)(o);return core_form.NG_VALIDATORS&&t===core_form.NG_VALIDATORS?to_factory_provider(t,(()=>new MockValidatorProxy(e))):core_form.NG_ASYNC_VALIDATORS&&t===core_form.NG_ASYNC_VALIDATORS?to_factory_provider(t,(()=>new MockAsyncValidatorProxy(e))):core_form.NG_VALUE_ACCESSOR&&t===core_form.NG_VALUE_ACCESSOR?to_factory_provider(t,(()=>new MockControlValueAccessorProxy(e))):void 0},processOwnUseExisting=(e,o,t)=>{const r=(0,func_get_type.A)(t);if(r!==core_form.NgControl&&r!==core_form.FormControlDirective)return t!==r&&(0,func_extract_forward_ref.A)(t.useExisting)===e?to_existing_provider(r,o):void 0},processProvider=(e,o,t,r)=>{const n=processTokens(o,t);if(n)return n;return processOwnUseExisting(e,o,t)||helper_mock_service.A.resolveProvider(t,r)},clone_providers=(e,o,t,r)=>{const n=[];let s;for(const c of(0,core_helpers.Bq)(t||[])){(0,func_get_type.A)(c)===core_form.NG_VALUE_ACCESSOR&&(s=!1);const t=processProvider(e,o,c,r);t&&n.push(t)}return{providers:n,setControlValueAccessor:s}},buildConfig=(e,o,t)=>({config:ng_mocks_universe.A.config.get(e),outputs:o.outputs,queryScanKeys:[],setControlValueAccessor:t}),decorate_declaration=(e,o,t,r)=>{const n=ng_mocks_universe.A.config.has("mockNgDefResolver");n||ng_mocks_universe.A.config.set("mockNgDefResolver",new core_def_stack);const s=Object.assign({},r);if(void 0!==t.exportAs&&(s.exportAs=t.exportAs),void 0!==t.selector&&(s.selector=t.selector),void 0!==t.standalone&&(s.standalone=t.standalone),t.standalone&&t.imports){const[,{imports:e}]=mock_ng_def({imports:t.imports,skipExports:!0});(null==e?void 0:e.length)&&(s.imports=e)}if(t.hostDirectives){const[,{hostDirectives:e}]=mock_ng_def({hostDirectives:t.hostDirectives,skipExports:!0});(null==e?void 0:e.length)&&(s.hostDirectives=e)}const{setControlValueAccessor:c,providers:_}=clone_providers(e,o,t.providers||[],ng_mocks_universe.A.config.get("mockNgDefResolver"));_.push(to_existing_provider(e,o)),s.providers=_;const{providers:i}=clone_providers(e,o,t.viewProviders||[],ng_mocks_universe.A.config.get("mockNgDefResolver"));i.length>0&&(s.viewProviders=i);const a=buildConfig(e,t,null!=c?c:-1!==helper_mock_service.A.extractMethodsFromPrototype(e.prototype).indexOf("writeValue"));decorate_mock(o,e,a),t.queries&&decorate_inputs(o,t.inputs,Object.keys(t.queries)),decorate_outputs(o,t.outputs),a.queryScanKeys=decorate_queries(o,t.queries),a.hostBindings=[];for(const[e]of t.hostBindings||[])-1===a.hostBindings.indexOf(e)&&a.hostBindings.push(e);a.hostListeners=[];for(const[e]of t.hostListeners||[])-1===a.hostListeners.indexOf(e)&&a.hostListeners.push(e);return n||ng_mocks_universe.A.config.delete("mockNgDefResolver"),s},getType=e=>(0,func_is_ng_def.p)(e,"p")?"pipe":(0,func_is_ng_def.p)(e,"d")?"directive":(0,func_is_ng_def.p)(e,"c")?"component":(0,func_is_ng_def.p)(e,"m")?"module":(0,func_is_ng_def.p)(e,"i")?"service":(0,func_is_ng_def.p)(e,"t")?"token":"",func_import_exists=(e,o)=>{if(null==e)throw new Error(`null / undefined has been passed into ${o}. Please check that its import is correct.`);if("MockPipe"===o&&(0,func_is_ng_def.p)(e,"p"))return;if("MockDirective"===o&&(0,func_is_ng_def.p)(e,"d"))return;if("MockComponent"===o&&(0,func_is_ng_def.p)(e,"c"))return;if("MockModule"===o&&(0,func_is_ng_def.p)(e,"m"))return;const t=getType(e);if(t&&"MockPipe"===o)throw new Error(`${o} accepts pipes, whereas ${(0,func_get_name.A)(e)} is a ${t}.`);if(t&&"MockDirective"===o)throw new Error(`${o} accepts directives, whereas ${(0,func_get_name.A)(e)} is a ${t}.`);if(t&&"MockComponent"===o)throw new Error(`${o} accepts components, whereas ${(0,func_get_name.A)(e)} is a ${t}.`);if(t&&"MockModule"===o)throw new Error(`${o} accepts modules, whereas ${(0,func_get_name.A)(e)} is a ${t}.`)};function isMockNgDef(e,o){return!!e.mockOf&&(!o||(0,func_is_ng_def.p)(e.mockOf,o))}const func_get_last_fixture=()=>{const e=(0,testing_.getTestBed)()._activeFixtures;return e[e.length-1]},return_cached_mock=e=>{let o;try{o=func_get_last_fixture().debugElement.injector.get(core_tokens.en).get(e)}catch(e){}return o||(o=ng_mocks_universe.A.cacheDeclarations.get(e)),e.__ngMocksResolutions&&ng_mocks_universe.A.config.has("mockNgDefResolver")&&ng_mocks_universe.A.config.get("mockNgDefResolver").merge(e.__ngMocksResolutions),o},get_mock=(e,o,t,r,n,s)=>{if(func_import_exists(e,t),isMockNgDef(e,o))return e;if(ng_mocks_universe.A.flags.has(r)&&ng_mocks_universe.A.cacheDeclarations.has(e))return return_cached_mock(e);const c=ng_mocks_universe.A.config.has("ngMocksDepsResolution");c||ng_mocks_universe.A.config.set("ngMocksDepsResolution",new Map);const _=(0,core_helpers.He)(n);return s(e,_),ng_mocks_universe.A.flags.has(r)&&ng_mocks_universe.A.cacheDeclarations.set(e,_),c||ng_mocks_universe.A.config.delete("ngMocksDepsResolution"),_},vcrArgs={read:core_.ViewContainerRef,static:!1},trArgs={read:core_.TemplateRef,static:!1},viewChildTemplate=(e,o)=>`
    `,isTemplateRefQuery=e=>!(e.isViewQuery||e.read&&e.read!==core_.TemplateRef||"string"!=typeof e.selector&&!e.read),generate_template=e=>{const o=[""];if(!e)return o.join("");for(const t of Object.keys(e)){const r=e[t];if(0!==t.indexOf("__mock")&&isTemplateRefQuery(r)){if("string"==typeof r.selector){const t=r.selector.replace(new RegExp("\\W","mg"),"_");e[`__vcrIf_key_${t}`]=new core_.ViewChild(`ngIf_key_${t}`,vcrArgs),e[`__trIf_key_${t}`]=new core_.ViewChild(`ngIf_key_${t}`,trArgs),e[`__mockView_key_${t}`]=new core_.ViewChild(`key_${t}`,vcrArgs),e[`__mockTpl_key_${t}`]=r,o.push(viewChildTemplate(t,"key"))}e[`__vcrIf_prop_${t}`]=new core_.ViewChild(`ngIf_prop_${t}`,vcrArgs),e[`__trIf_prop_${t}`]=new core_.ViewChild(`ngIf_prop_${t}`,trArgs),e[`__mockView_prop_${t}`]=new core_.ViewChild(`prop_${t}`,vcrArgs),o.push(viewChildTemplate(t,"prop"))}}return o.join("")},get_key=e=>{if("string"==typeof e)return["key",`__mockTpl_key_${e}`,e,void 0];const[o,...t]=e;return["prop",o,o,t.length>0?t:void 0]},mixRenderPrepareVcr=(e,o,t,r)=>{const n=e[`__vcrIf_${o}_${t}`],s=e[`__trIf_${o}_${t}`];return n&&s&&!e[`ngMocksRender_${o}_${t}`]&&(e[`ngMocksRender_${o}_${t}`]=n.createEmbeddedView(s,{}),r.detectChanges()),e[`__mockView_${o}_${t}`]},mixRenderReorderViews=(e,o,t)=>{for(const e of o.splice(t+1))e.destroy();let r=0;for(const t of o)t&&(e.move(t,r),r+=1)},mixRenderApplyContext=(e,o)=>{for(const o of Object.keys(e.context))e.context[o]=void 0;for(const t of Object.keys(o))e.context[t]=o[t];e.markForCheck()},mixRenderHandleViews=(e,o,t,r,n,s)=>{let c=-1;for(const o of t)if(c+=1,r[c]=r[c]||void 0,(!n||-1!==n.indexOf(c))&&o){if(!(o instanceof core_.TemplateRef))throw new Error("Cannot find TemplateRef");r[c]||(r[c]=e.createEmbeddedView(o,{})),mixRenderApplyContext(r[c],s)}return o.detectChanges(),c},mixRender=(e,o)=>{(0,core_define_property.A)(e,"__render",((t,r,n)=>{const[s,c,_,i]=get_key(t),a=mixRenderPrepareVcr(e,s,_,o);if(!a)return;const l=e[c],p=l instanceof core_.QueryList?l.toArray():[l],u=e[`ngMocksRender_${s}_${_}_views`]||[],d=mixRenderHandleViews(a,o,p,u,i,Object.assign(Object.assign({},n),{$implicit:r}));mixRenderReorderViews(a,u,d),e[`ngMocksRender_${s}_${_}_views`]=u,o.detectChanges()}))},mixHideHandler=(e,o,t,r)=>{const n=e[`ngMocksRender_${o}_${t}_views`];let s=-1;for(const e of n)s+=1,r&&-1===r.indexOf(s)||!e||(e.destroy(),n[s]=void 0)},mixHide=(e,o)=>{(0,core_define_property.A)(e,"__hide",(t=>{const[r,,n,s]=get_key(t);e[`ngMocksRender_${r}_${n}`]&&(mixHideHandler(e,r,n,s),s||(e[`ngMocksRender_${r}_${n}`].destroy(),e[`ngMocksRender_${r}_${n}`]=void 0),o.detectChanges())}))};class ComponentMockBase extends LegacyControlValueAccessor{constructor(e,o,t){super(e,o),func_is_mock(this)&&(mixRender(this,t),mixHide(this,t))}ngAfterViewInit(){const e=this.__ngMocksConfig.config;if(!this.__rendered&&e&&e.render){for(const o of Object.keys(e.render)){const{$implicit:t,variables:r}=!0===e.render[o]?{$implicit:void 0,variables:{}}:e.render[o];this.__render(o,t,r)}this.__rendered=!0}}}(0,core_define_property.A)(ComponentMockBase,"parameters",[[core_.Injector],[core_form.NgControl||(()=>{}),new core_.Optional,new core_.Self],[core_.ChangeDetectorRef]]);const decorateClass=(e,o)=>{const t=core_reflect_directive_resolve(e);(0,core_.Component)(decorate_declaration(e,o,t,{template:generate_template(t.queries)}))(o)};function MockComponents(...e){return e.map(MockComponent)}function MockComponent(e){return get_mock(e,"c","MockComponent","cacheComponent",ComponentMockBase,decorateClass)}class DirectiveMockBase extends LegacyControlValueAccessor{constructor(e,o,t,r,n=null,s=null){super(e,o),this.__ngMocksInstall(r,t,n,s)}ngOnInit(){const e=this.__ngMocksConfig.config;if(null==e?void 0:e.render){const{$implicit:o,variables:t}=!0===e.render?{$implicit:void 0,variables:{}}:e.render;this.__render(o,t)}}__ngMocksInstall(e,o,t,r){(0,core_define_property.A)(this,"__element",t),(0,core_define_property.A)(this,"__template",r),(0,core_define_property.A)(this,"__viewContainer",e),(0,core_define_property.A)(this,"__vcr",e),(0,core_define_property.A)(this,"__cdr",o),(0,core_define_property.A)(this,"__isStructural",r&&e),(0,core_define_property.A)(this,"__render",((t,n)=>{e&&r&&(e.clear(),e.createEmbeddedView(r,Object.assign(Object.assign({},n),{$implicit:t})),o.detectChanges())}))}}(0,core_define_property.A)(DirectiveMockBase,"parameters",[[core_.Injector],[core_form.NgControl||(()=>{}),new core_.Optional,new core_.Self],[core_.ChangeDetectorRef],[core_.ViewContainerRef],[core_.ElementRef,new core_.Optional,new core_.Self],[core_.TemplateRef,new core_.Optional,new core_.Self]]);const mock_directive_decorateClass=(e,o)=>{const t=core_reflect_directive_resolve(e),r=decorate_declaration(e,o,t,{});(0,core_.Directive)(r)(o)};function MockDirectives(...e){return e.map(MockDirective)}function MockDirective(e){return get_mock(e,"d","MockDirective","cacheDirective",DirectiveMockBase,mock_directive_decorateClass)}const core_reflect_pipe_resolve=e=>core_reflect_body_catch((e=>{const o=(0,collect_declarations.A)(e);if(o.Pipe)return o.Pipe;throw new Error("Cannot resolve declarations")}))(e);function MockPipes(...e){return e.map((e=>MockPipe(e,void 0)))}const getMockClass=(e,o)=>{var t;const r=ng_mocks_universe.A.config.get(e),n=null!=o?o:null===(t=null==r?void 0:r.defValue)||void 0===t?void 0:t.transform,s=(0,core_helpers.He)(Mock);return(0,core_.Pipe)(core_reflect_pipe_resolve(e))(s),decorate_mock(s,e,{init:e=>{n&&(e.transform=n),e.transform||helper_mock_service.A.mock(e,"transform",`${(0,func_get_name.A)(e)}.transform`)},transform:n}),s};function MockPipe(e,o){if(func_import_exists(e,"MockPipe"),isMockNgDef(e,"p"))return e;if(ng_mocks_universe.A.flags.has("cachePipe")&&ng_mocks_universe.A.cacheDeclarations.has(e))return return_cached_mock(e);const t=getMockClass(e,o);return ng_mocks_universe.A.flags.has("cachePipe")&&ng_mocks_universe.A.cacheDeclarations.set(e,t),t}const flagMock=e=>"mock"===e&&ng_mocks_universe.A.flags.has("skipMock"),flagKeep=e=>"keep"===e&&!ng_mocks_universe.A.flags.has("skipMock"),flagReplace=e=>"replace"===e&&!ng_mocks_universe.A.flags.has("skipMock"),flagNever=e=>-1!==core_config.A.neverMockModule.indexOf((0,func_get_name.A)(e))&&!ng_mocks_universe.A.flags.has("skipMock"),preProcessFlags=e=>{let o=!1,t=!0;ng_mocks_universe.A.flags.has("hasRootModule")?t=!1:ng_mocks_universe.A.flags.add("hasRootModule");const r=ng_mocks_universe.A.getResolution(e);return flagMock(r)&&(o=!0,ng_mocks_universe.A.flags.delete("skipMock")),flagNever(e)&&(o=!0,ng_mocks_universe.A.flags.add("skipMock")),t||!flagKeep(r)&&!flagReplace(r)||(o=!0,ng_mocks_universe.A.flags.add("skipMock")),{isRootModule:t,toggleSkipMockFlag:o}},postProcessFlags=({isRootModule:e,toggleSkipMockFlag:o})=>{o&&ng_mocks_universe.A.flags.has("skipMock")?ng_mocks_universe.A.flags.delete("skipMock"):o&&!ng_mocks_universe.A.flags.has("skipMock")&&ng_mocks_universe.A.flags.add("skipMock"),e&&ng_mocks_universe.A.flags.delete("hasRootModule")},extractModuleAndProviders=e=>{let o,t;return(0,func_is_ng_module_def_with_providers.h)(e)?(o=e.ngModule,e.providers&&(t=e.providers)):o=e,{ngModule:o,ngModuleProviders:t}},getExistingMockModule=(e,o)=>{var t;if(isMockNgDef(e,"m"))return e;if(ng_mocks_universe.A.flags.has("cacheModule")&&ng_mocks_universe.A.cacheDeclarations.has(e))return return_cached_mock(e);if(!o&&"mock"!==(null===(t=ng_mocks_universe.A.config.get("ngMocksDepsResolution"))||void 0===t?void 0:t.get(e))&&ng_mocks_universe.A.hasBuildDeclaration(e)){const o=ng_mocks_universe.A.getBuildDeclaration(e);if((0,func_is_ng_def.p)(o,"m")&&o!==e)return o}},detectMockModule=(e,o)=>{const[t,r,n]=o?[!1]:mock_ng_def(core_reflect_module_resolve(e),e);if(n&&(0,core_define_property.A)(e,"__ngMocksResolutions",n),t){const o=ng_mocks_universe.A.flags.has("skipMock")?e:Mock,t=(0,core_helpers.He)(o);return(0,core_.NgModule)(r)(t),decorate_mock(t,e),t}return o||e},getMockProviders=e=>{if(e){const[o,t]=mock_ng_def({providers:e,skipExports:!0});return o?t.providers:e}},generateReturn=(e,o,t,r,n)=>r===o&&n===t?e:(0,func_is_ng_module_def_with_providers.h)(e)?Object.assign({ngModule:r},n?{providers:n}:{}):r;function MockModule(e){var o;func_import_exists(e,"MockModule");const{ngModule:t,ngModuleProviders:r}=extractModuleAndProviders(e),n=preProcessFlags(t);try{const s=detectMockModule(t,getExistingMockModule(t,n.isRootModule));ng_mocks_universe.A.flags.has("cacheModule")&&ng_mocks_universe.A.cacheDeclarations.set(t,s),ng_mocks_universe.A.flags.has("skipMock")&&(null===(o=ng_mocks_universe.A.config.get("ngMocksDepsSkip"))||void 0===o||o.add(s));const c=getMockProviders(r);return generateReturn(e,t,r,s,c)}finally{postProcessFlags(n)}}const processDefMap=[["c",MockComponent],["d",MockDirective],["p",MockPipe]],processDef=e=>{if((0,func_is_ng_def.p)(e,"m")||(0,func_is_ng_module_def_with_providers.h)(e))return MockModule(e);if(ng_mocks_universe.A.hasBuildDeclaration(e))return ng_mocks_universe.A.getBuildDeclaration(e);if(ng_mocks_universe.A.flags.has("skipMock")&&"mock"!==ng_mocks_universe.A.getResolution(e))return e;for(const[o,t]of processDefMap)if((0,func_is_ng_def.p)(e,o))return t(e)},createResolveProvider=(e,o)=>t=>helper_mock_service.A.resolveProvider(t,e,o),createResolveWithProviders=(e,o)=>(0,func_is_ng_module_def_with_providers.h)(o)&&(0,func_is_ng_module_def_with_providers.h)(e),createResolveExisting=(e,o,t)=>{const r=o.get(e);return e!==r&&t(),r},createResolveExcluded=(e,o,t)=>{o.set(e,void 0),t()},createResolve=(e,o)=>t=>{var r;if(e.has(t))return createResolveExisting(t,e,o);const n=(0,func_get_type.A)(t);if(ng_mocks_universe.A.isExcludedDef(n))return createResolveExcluded(t,e,o);ng_mocks_universe.A.touches.add(n);const s=processDef(t);return createResolveWithProviders(t,s)&&e.set(t.ngModule,s.ngModule),ng_mocks_universe.A.flags.has("skipMock")&&(null===(r=ng_mocks_universe.A.config.get("ngMocksDepsSkip"))||void 0===r||r.add(s)),e.set(t,s),o(s!==t),s},create_resolvers=(e,o)=>({resolve:createResolve(o,e),resolveProvider:createResolveProvider(o,e)}),mark_providers=e=>{for(const o of(0,core_helpers.Bq)(null!=e?e:[])){const e=(0,func_get_type.A)(o);mark_exported(e)}},flatToExisting=(e,o)=>(0,core_helpers.Bq)(e).map(o).filter((e=>!!e)),configureProcessMetaKeys=(e,o)=>[["declarations",e],["hostDirectives",o=>{const t=(0,func_get_type.A)(o),r=e(t);return r===t?o:o==t?r:Object.assign(Object.assign({},o),{directive:r})}],["imports",e],["entryComponents",e],["bootstrap",e],["providers",o],["viewProviders",o],["exports",e],["schemas",e=>e]],processMeta=(e,o,t)=>{var r;const n={},s=configureProcessMetaKeys(o,t),c=ng_mocks_universe.A.flags.has("cachePipe");c||ng_mocks_universe.A.flags.add("cachePipe");for(const[o,t]of s)(null===(r=e[o])||void 0===r?void 0:r.length)&&(n[o]=flatToExisting(e[o],t));return e.skipMarkProviders||(mark_providers(n.providers),mark_providers(n.viewProviders)),c||ng_mocks_universe.A.flags.delete("cachePipe"),n},resolveDefForExport=(e,o,t,r)=>{const n=ng_mocks_universe.A.config.get(r)||{},s=(0,func_get_type.A)(e),c=o(s);if(!c)return;const _=ng_mocks_universe.A.config.get(s);return(null==_?void 0:_.export)&&r&&!n.export&&ng_mocks_universe.A.config.set(r,Object.assign(Object.assign({},n),{export:!0})),!t||n.exportAll||(null==_?void 0:_.export)?(mark_exported(s,r),c):void 0},skipAddExports=(e,o)=>!e||!!o.exports&&-1!==o.exports.indexOf(e),addExports=(e,o,t,r,n)=>{const s=ng_mocks_universe.A.flags.has("skipMock")||ng_mocks_universe.A.flags.has("correctModuleExports");for(const c of(0,core_helpers.Bq)([t.imports||[],t.declarations||[]])){const t=resolveDefForExport(c,e,s,n);skipAddExports(t,r)||(o(),r.exports=r.exports||[],r.exports.push(t))}},mock_ng_def=(e,o)=>{const t=ng_mocks_universe.A.config.has("mockNgDefResolver");t||ng_mocks_universe.A.config.set("mockNgDefResolver",new core_def_stack),ng_mocks_universe.A.config.get("mockNgDefResolver").push();let r=!ng_mocks_universe.A.flags.has("skipMock");const n=(e=!0)=>{r=r||e},{resolve:s,resolveProvider:c}=create_resolvers(n,ng_mocks_universe.A.config.get("mockNgDefResolver")),_=processMeta(e,s,c);e.skipExports||addExports(s,n,e,_,o);for(const e of o&&_.exports?(0,core_helpers.Bq)(_.exports):[])mark_exported(e,o);const i=ng_mocks_universe.A.config.get("mockNgDefResolver").pop();return t||ng_mocks_universe.A.config.delete("mockNgDefResolver"),[r,_,i]},get_override_def=e=>{if(!e)return;const o=ng_mocks_universe.A.flags.has("skipMock");o||ng_mocks_universe.A.flags.add("skipMock");const[t,r]=mock_ng_def(e);return o||ng_mocks_universe.A.flags.delete("skipMock"),t?r:void 0},skip_override=(e,o,t,r)=>!!ng_mocks_universe.A.cacheDeclarations.has(r)||!(!e.has(t)||r!==o.get(t)),create_ng_mocks_overrides_token=(e,o)=>{const t=new Map;for(const r of(0,core_helpers.LG)(ng_mocks_universe.A.touches)){const n=r,s=ng_mocks_universe.A.getBuildDeclaration(n)||n;if(skip_override(e,o,n,s))continue;const c=core_reflect_meta(s),_=get_override_def(c);if(!_)continue;const i={};for(const e of Object.keys(_))i[e]=c[e];t.set(s,[{set:_},{set:i}])}return{provide:core_tokens.So,useValue:t}},create_ng_mocks_token=()=>{const e=new Map;for(const[o,t]of[...(0,core_helpers.Nn)(ng_mocks_universe.A.builtProviders),...(0,core_helpers.Nn)(ng_mocks_universe.A.builtDeclarations),...(0,core_helpers.Nn)(ng_mocks_universe.A.cacheDeclarations),...(0,core_helpers.Nn)(ng_mocks_universe.A.cacheProviders)])e.has(o)||e.set(o,t);return{provide:core_tokens.en,useValue:e}},create_ng_mocks_touches_token=()=>{const e=new Set;for(const o of(0,core_helpers.LG)(ng_mocks_universe.A.touches)){const t=o;let r=ng_mocks_universe.A.getBuildDeclaration(t);void 0===r&&(r=t),e.add(t),e.add(r)}return{provide:core_tokens.Em,useValue:e}};var helper_create_clone=__webpack_require__(365);class EntryComponentsModule{constructor(e,o){if(!o)return;const t=o.resolveComponentFactory;o.resolveComponentFactory=(0,helper_create_clone.A)(t,void 0,void 0,((r,...n)=>{var s;return t.apply(o,[null!==(s=e.get(r))&&void 0!==s?s:r,...n])}))}}(0,core_define_property.A)(EntryComponentsModule,"parameters",[[core_tokens.en],[core_.ComponentFactoryResolver,new core_.Optional]]);class IvyModule{}(0,core_.NgModule)()(IvyModule);const handle_entry_components=e=>{const o=[];for(const t of e.declarations)(0,func_is_ng_def.p)(t,"c")&&o.push(t);const t=(0,core_helpers.He)(EntryComponentsModule);(0,core_.NgModule)({entryComponents:IvyModule.ɵmod?[]:o})(t),e.imports.push(t)};var helper_use_factory=__webpack_require__(465),core_reflect_parameters_resolve=__webpack_require__(749),common_x=e=>{var o={};return __webpack_require__.d(o,e),o},common_y=e=>()=>e;const common_namespaceObject=common_x({DOCUMENT:()=>__WEBPACK_EXTERNAL_MODULE__angular_common_d12e0fe1__.DOCUMENT}),skipResolution=e=>{const o=ng_mocks_universe.A.getResolution(e);return"keep"===o||"exclude"===o||"mock"!==o&&void 0},skipSystem=e=>{if(!e||e===common_namespaceObject.DOCUMENT||ng_mocks_universe.A.touches.has(e))return!0;const o=skipResolution(e);return void 0!==o?o:"function"==typeof e&&-1!==core_config.A.neverMockProvidedFunction.indexOf(e.name)||!(!(0,func_is_ng_injection_token.S)(e)||-1===core_config.A.neverMockToken.indexOf(e.toString()))},skip_dep=e=>{if(skipSystem(e))return!0;const o=core_reflect_provided_in(e);return!("function"!=typeof e||o&&"platform"!==o)},add_def_to_root_provider_parameters=(e,o,t)=>{skip_dep(t)||!o.has(core_tokens.gG)&&ng_mocks_universe.A.config.get("ngMocksDepsSkip").has(t)||e.add(t)},check_root_provider_dependency=(e,o,t)=>{"function"==typeof e&&-1===t.indexOf(e)&&(t.push(e),o.push(e))},extract_dep=e=>{if(!e)return;let o;for(const t of e)t&&"object"==typeof t&&t.token&&(o=t.token),o||!t||"object"==typeof t&&t.ngMetadataName||(o=t);return(0,func_extract_forward_ref.A)(o)},get_root_providers_data=()=>{const e=[(0,core_helpers.LG)(ng_mocks_universe.A.config.get("ngMocksDepsSkip")),(0,core_helpers.LG)(ng_mocks_universe.A.config.get("ngMocksDeps")),(0,core_helpers.LG)(ng_mocks_universe.A.touches)];return{buckets:e,touched:[].concat(...e)}},handle_provided_in_dependency=e=>{if(ng_mocks_universe.A.touches.has(e))return;const o=core_reflect_provided_in(e);o&&ng_mocks_universe.A.config.get("ngMocksDepsSkip").has(o)&&ng_mocks_universe.A.config.get("ngMocksDepsSkip").add(e)},skip_root_provider_dependency=e=>!!skip_dep(e)||ng_mocks_universe.A.config.get("ngMocksDepsSkip").has(e),get_root_provider_parameters=e=>{const o=new Set,{buckets:t,touched:r}=get_root_providers_data();for(const n of t)for(const t of n){add_def_to_root_provider_parameters(o,e,t);for(const s of(0,core_reflect_parameters_resolve.A)(t)){const c=extract_dep(s);handle_provided_in_dependency(c),skip_root_provider_dependency(c)||(check_root_provider_dependency(c,r,n),e.has(core_tokens.gG)||!ng_mocks_universe.A.config.get("ngMocksDepsSkip").has(t)?o.add(c):ng_mocks_universe.A.config.get("ngMocksDepsSkip").add(c))}}return o},handle_root_providers=(e,{keepDef:o,mockDef:t},r)=>{const n=o.has(core_tokens.gG)?new Set:get_root_provider_parameters(t);if(n.size>0)for(const o of(0,core_helpers.LG)(n)){const t=(0,helper_resolve_provider.A)(o,r);if(t)e.providers.push(t);else if((0,func_is_ng_injection_token.S)(o)){const t=ng_mocks_universe.A.config.has("ngMocksMulti")&&ng_mocks_universe.A.config.get("ngMocksMulti").has(o);e.providers.push((0,helper_use_factory.A)(o,(()=>t?[]:void 0)))}}},init_module=(e,o)=>{var t;const r=null!==(t=ng_mocks_universe.A.config.get("mockNgDefResolver").get(e))&&void 0!==t?t:ng_mocks_universe.A.getBuildDeclaration(e),n=o.has(e)?o.get(e):void 0;return n?{ngModule:r,providers:n}:r},skipDef=e=>ng_mocks_universe.A.touches.has(e)||(0,func_is_ng_def.p)(e)||(0,func_is_ng_injection_token.S)(e)||"string"==typeof e,handleDef=({imports:e,declarations:o,providers:t},r,n)=>{skipDef(r)||error_jest_mock(r);let s=!1;if((0,func_is_ng_def.p)(r,"m")){const o=init_module(r,n);if(e.push(o),s=!0,"object"==typeof o&&o.providers)for(const e of(0,core_helpers.Bq)(o.providers))ng_mocks_universe.A.touches.add((0,func_get_type.A)(e))}if(((0,func_is_ng_def.p)(r,"c")||(0,func_is_ng_def.p)(r,"d")||(0,func_is_ng_def.p)(r,"p"))&&((isStandalone(r)?e:o).push(ng_mocks_universe.A.getBuildDeclaration(r)),s=!0),(0,func_is_ng_def.p)(r,"i")||!(0,func_is_ng_def.p)(r)){const e=ng_mocks_universe.A.builtProviders.get(r);e&&"string"!=typeof e&&!1===(0,func_is_ng_def.p)(e,"t")&&(t.push(e),s=!0)}s&&(mark_exported(r),ng_mocks_universe.A.touches.add(r))},isExportedOnRoot=(e,o,t)=>{const r=o.get(e),n=t.get(e)||{};if((0,func_is_ng_def.p)(e,"m")&&n.onRoot)return e;if(!(null==r?void 0:r.exported))return e;for(const e of(0,core_helpers.LG)(r.exported)){const r=isExportedOnRoot(e,o,t);if(r)return r}},moveModulesUp=(e,o)=>{const t=(0,func_is_ng_def.p)(e,"m"),r=(0,func_is_ng_def.p)(o,"m");return t&&r?0:t?-1:r?1:0},init_ng_modules=({configDefault:e,keepDef:o,mockDef:t,replaceDef:r},n)=>{const s={imports:[],declarations:[],providers:[]},c=[],_=[],i=[...(0,core_helpers.LG)(t),...(0,core_helpers.LG)(o),...(0,core_helpers.LG)(r)];i.sort(moveModulesUp);for(const o of i){const t=(0,func_is_ng_def.p)(o,"m")&&n.has(o)?o:isExportedOnRoot(o,ng_mocks_universe.A.configInstance,ng_mocks_universe.A.config);if(!t||-1!==c.indexOf(t))continue;const r=ng_mocks_universe.A.config.get(t)||{__set:!0};c.push(t),r.onRoot=r.onRoot||!r.dependency,r.__set&&(r.__set=void 0,ng_mocks_universe.A.config.set(t,r)),(0,func_is_ng_def.p)(t,"m")&&r.onRoot?handleDef(s,t,n):r.dependency||!r.export||!(0,func_is_ng_def.p)(t,"i")&&(0,func_is_ng_def.p)(t)?!r.dependency&&r.export?handleDef(s,t,n):ng_mocks_universe.A.touches.has(t)||r.dependency?!r.dependency||!e.dependency||"root"===core_reflect_provided_in(t)||"object"==typeof t&&t.__ngMocksSkip||_.push(t):handleDef(s,t,n):(handleDef(s,t,n),mark_providers([t]))}const a=ng_mocks_universe.A.global.get("flags");for(const e of _){if(ng_mocks_universe.A.touches.has(e))continue;const o=[`MockBuilder has found a missing dependency: ${(0,func_get_name.A)(e)}.`,"It means no module provides it.",'Please, use the "export" flag if you want to add it explicitly.',"https://ng-mocks.sudo.eu/api/MockBuilder#export-flag"].join(" ");if("warn"===a.onMockBuilderMissingDependency)console.warn(o);else if("throw"===a.onMockBuilderMissingDependency)throw new Error(o)}return s},funcExtractDeps=(e,o,t=!1)=>{const r=(0,collect_declarations.A)(e),n=getNgType(e);if(!n||"Injectable"===n)return o;const s=r[n];for(const e of core_config.A.dependencies)if(s[e])for(const r of(0,core_helpers.Bq)(s[e])){const e=(0,func_get_type.A)(r);o.has(e)||(o.add(e),t&&funcExtractDeps(e,o))}return o},init_exclude_def=e=>{const o=ng_mocks_universe.A.builtDeclarations,t=ng_mocks_universe.A.builtProviders,r=ng_mocks_universe.A.config.get("ngMocksDepsResolution");for(const n of(0,core_helpers.LG)(e))o.set(n,null),t.set(n,null),r.set(n,"exclude")},init_keep_def=(e,o)=>{const t=new Set,r=ng_mocks_universe.A.builtDeclarations,n=ng_mocks_universe.A.builtProviders,s=ng_mocks_universe.A.config.get("ngMocksDepsResolution");for(const c of(0,core_helpers.LG)(e))r.set(c,c),n.set(c,c),s.set(c,"keep"),o.get(c).shallow&&funcExtractDeps(c,t);return t};var mock_provider=__webpack_require__(415);const createInstance=(e,o,t)=>t.precise?o:(0,mock_helper_stub.A)(e,o),try_mock_provider=(e,o)=>{if((0,func_is_ng_def.p)(e,"i")&&o.has(e)){const t=ng_mocks_universe.A.config.get(e),r=o.get(e);ng_mocks_universe.A.builtProviders.set(e,(0,helper_use_factory.A)(e,void 0,(e=>createInstance(e,r,t))))}else(0,func_is_ng_def.p)(e,"i")&&ng_mocks_universe.A.builtProviders.set(e,(0,mock_provider.A)(e,!0));if(!(0,func_is_ng_def.p)(e)&&o.has(e)){const t=o.get(e);ng_mocks_universe.A.builtProviders.set(e,(0,helper_use_factory.A)(e,void 0,(()=>t)))}else(0,func_is_ng_def.p)(e)||ng_mocks_universe.A.builtProviders.set(e,(0,mock_provider.A)(e,!0))},init_mock_declarations=(e,o)=>{const t=ng_mocks_universe.A.builtDeclarations,r=ng_mocks_universe.A.config.get("ngMocksDepsResolution");for(const n of(0,core_helpers.LG)(e)){const e=!ng_mocks_universe.A.touches.has(n);r.set(n,"mock"),t.set(n,void 0),try_mock_provider(n,o),e&&ng_mocks_universe.A.touches.delete(n)}},try_mock_declaration=e=>{void 0===ng_mocks_universe.A.builtDeclarations.get(e)&&((0,func_is_ng_def.p)(e,"c")&&ng_mocks_universe.A.builtDeclarations.set(e,MockComponent(e)),(0,func_is_ng_def.p)(e,"d")&&ng_mocks_universe.A.builtDeclarations.set(e,MockDirective(e)),(0,func_is_ng_def.p)(e,"p")&&ng_mocks_universe.A.builtDeclarations.set(e,MockPipe(e)))},init_modules=(e,o,t,r)=>{var n,s,c,_,i;const a=new Map;for(const l of[...(0,core_helpers.LG)(e),...(0,core_helpers.LG)(o),...(0,core_helpers.LG)(t)]){const e=(0,collect_declarations.A)(l),t=[...null!==(n=r.get(l))&&void 0!==n?n:[],...null!==(c=null===(s=e.Component)||void 0===s?void 0:s.providers)&&void 0!==c?c:[],...null!==(i=null===(_=e.Directive)||void 0===_?void 0:_.providers)&&void 0!==i?i:[]],p=!ng_mocks_universe.A.touches.has(l);o.has(l)||ng_mocks_universe.A.flags.add("skipMock");const u=(0,func_is_ng_def.p)(l,"m");if(t.length>0){const[,e]=mock_ng_def({providers:t,skipMarkProviders:!u,skipExports:!0});a.set(l,e.providers)}u&&ng_mocks_universe.A.builtDeclarations.set(l,MockModule(l)),ng_mocks_universe.A.flags.delete("skipMock"),p&&ng_mocks_universe.A.touches.delete(l)}for(const e of(0,core_helpers.LG)(o))try_mock_declaration(e);return a},init_replace_def=(e,o)=>{const t=ng_mocks_universe.A.builtDeclarations,r=ng_mocks_universe.A.config.get("ngMocksDepsResolution");for(const n of(0,core_helpers.LG)(e))t.set(n,o.get(n)),r.set(n,"replace")},init_universe=({configDef:e,defProviders:o,defValue:t,excludeDef:r,keepDef:n,mockDef:s,replaceDef:c})=>{ng_mocks_universe.A.flags.add("cachePipe"),ng_mocks_universe.A.config.set("ngMocksMulti",new Set),ng_mocks_universe.A.config.set("ngMocksDeps",new Set),ng_mocks_universe.A.config.set("ngMocksDepsSkip",new Set),ng_mocks_universe.A.config.set("ngMocksDepsResolution",new Map);const _=init_keep_def(n,e);for(const e of(0,core_helpers.LG)(_))ng_mocks_universe.A.touches.add(e);for(const e of(0,core_helpers.LG)(n))_.add(e),funcExtractDeps(e,_,!0);for(const e of(0,core_helpers.LG)(s))_.add(e),funcExtractDeps(e,_,!0);for(const e of(0,core_helpers.LG)(c))_.add(e),funcExtractDeps(e,_,!0);for(const o of(0,core_helpers.LG)(_)){if(e.has(o))continue;const _=ng_mocks_universe.A.getResolution(o);"replace"===_?(c.add(o),t.set(o,ng_mocks_universe.A.getBuildDeclaration(o))):"keep"===_?n.add(o):"exclude"===_?r.add(o):("mock"===_||ng_mocks_universe.A.touches.has(o))&&s.add(o),e.set(o,ng_mocks_universe.A.touches.has(o)?{dependency:!0,__internal:!0}:{})}for(const[o,r]of(0,core_helpers.Nn)(e))ng_mocks_universe.A.config.set(o,Object.assign(Object.assign(Object.assign({},ng_mocks_universe.A.getConfigMock().get(o)),r),{defValue:t.get(o)}));return init_replace_def(c,t),init_exclude_def(r),init_mock_declarations(s,t),init_modules(n,s,c,o)},parse_mock_arguments=(e,o,t,r)=>{let n=e===o?r:o,s=null!=t?t:o!==r&&"object"==typeof o?o:void 0;return(0,func_is_ng_def.p)(e,"p")&&"function"==typeof o&&o!==e&&!(0,func_is_ng_def.p)(o,"p")?(n={transform:o},s=t):!(0,func_is_ng_def.p)(e,"i")&&(0,func_is_ng_def.p)(e)||(s=t),n=n===s?r:n,{config:s,mock:n}},parse_provider=e=>{const o=(0,func_get_type.A)(e);return{multi:o!==e&&e.multi,provide:o}};var __awaiter=function(e,o,t,r){return new(t||(t=Promise))((function(n,s){function c(e){try{i(r.next(e))}catch(e){s(e)}}function _(e){try{i(r.throw(e))}catch(e){s(e)}}function i(e){var o;e.done?n(e.value):(o=e.value,o instanceof t?o:new t((function(e){e(o)}))).then(c,_)}i((r=r.apply(e,o||[])).next())}))};const normaliseModule=e=>(0,func_is_ng_module_def_with_providers.h)(e)?{def:e.ngModule,providers:e.providers}:{def:e,providers:void 0},generateProviderValue=(e,o,t)=>t?[...Array.isArray(o)?o:[],e]:e,defaultMock={};class MockBuilderPromise{constructor(e){this.configDefault=e,this.beforeCC=new Set,this.configDef=new Map,this.defProviders=new Map,this.defValue=new Map,this.excludeDef=new Set,this.keepDef=new Set,this.mockDef=new Set,this.providerDef=new Map,this.replaceDef=new Set,this.stash=new MockBuilderStash,"undefined"!=typeof Symbol&&(this[Symbol.toStringTag]="Promise")}beforeCompileComponents(e){return this.beforeCC.add(e),this}build(){this.stash.backup();const e=new core_def_stack;ng_mocks_universe.A.config.set("mockNgDefResolver",e),ng_mocks_universe.A.flags.add("hasRootModule");try{const o=this.combineParams(),t=init_ng_modules(o,init_universe(o));return add_requested_providers(t,o,e),handle_root_providers(t,o,e),handle_entry_components(t),apply_platform_modules(),t.providers.push(create_ng_mocks_token(),create_ng_mocks_touches_token(),create_ng_mocks_overrides_token(this.replaceDef,this.defValue),MockBuilder),t}finally{ng_mocks_universe.A.flags.delete("hasRootModule"),ng_mocks_universe.A.config.delete("mockNgDefResolver"),this.stash.restore()}}catch(e){return __awaiter(this,void 0,void 0,(function*(){return this.then().catch(e)}))}exclude(e){return this.wipe(e),this.excludeDef.add(e),this.setConfigDef(e),this}finally(e){return __awaiter(this,void 0,void 0,(function*(){return this.then().finally(e)}))}keep(e,o){const{def:t,providers:r}=normaliseModule(e),n=this.keepDef.has(t)?this.defProviders.get(t):[];return this.wipe(t),this.keepDef.add(t),r&&this.defProviders.set(t,[...n||[],...r]),this.setConfigDef(t,o),this}mock(e,o=defaultMock,t){const{def:r,providers:n}=normaliseModule(e),{config:s,mock:c}=parse_mock_arguments(r,o,t,defaultMock);if((0,func_is_ng_def.p)(c)&&(0,func_is_ng_def.p)(e)&&!(0,func_is_ng_def.p)(e,"t"))throw new Error([`MockBuilder.mock(${(0,func_get_name.A)(e)}) received a class when its shape is expected.`,"Please try ngMocks.defaultMock instead."].join(" "));const _=this.mockDef.has(r)?this.defProviders.get(r):[];return this.wipe(r),this.mockDef.add(r),n&&this.defProviders.set(r,[..._||[],...n]),this.setDefValue(r,c),this.setConfigDef(r,s),this}provide(e){for(const o of(0,core_helpers.Bq)(e)){const{provide:e,multi:t}=parse_provider(o),r=this.providerDef.has(e)?this.providerDef.get(e):[];this.providerDef.set(e,generateProviderValue(o,r,t))}return this}replace(e,o,t){if(!(0,func_is_ng_def.p)(o)||!(0,func_is_ng_def.p)(e)||(0,func_is_ng_def.p)(o,"i")||(0,func_is_ng_def.p)(e,"i"))throw new Error("Cannot replace the declaration, both have to be a Module, a Component, a Directive or a Pipe, for Providers use `.mock` or `.provide`");return this.wipe(e),this.replaceDef.add(e),this.defValue.set(e,o),this.setConfigDef(e,t),this}then(e,o){return __awaiter(this,void 0,void 0,(function*(){const t=new Promise((e=>{const o=testing_.TestBed.configureTestingModule(this.build());for(const e of(0,core_helpers.LG)(this.beforeCC))e(o);o.compileComponents().then((()=>{e({testBed:o})}))}));return t.then(e,o)}))}combineParams(){return{configDef:this.configDef,configDefault:this.configDefault,defProviders:this.defProviders,defValue:this.defValue,excludeDef:this.excludeDef,keepDef:this.keepDef,mockDef:this.mockDef,providerDef:this.providerDef,replaceDef:this.replaceDef}}setConfigDef(e,o){!o&&this.configDef.has(e)||this.configDef.set(e,null!=o?o:this.configDefault)}setDefValue(e,o){o===defaultMock?this.defValue.delete(e):this.defValue.set(e,o)}wipe(e){this.defProviders.delete(e),this.defValue.delete(e),this.excludeDef.delete(e),this.keepDef.delete(e),this.mockDef.delete(e),this.providerDef.delete(e),this.replaceDef.delete(e)}}const objectsDiffer=(e,o)=>{const t=Object.keys(e),r=Object.keys(o);if(t.length!==r.length)return!0;for(const r of t)if(e[r]!==o[r])return!0;return!1},equal_variables=(e,o)=>e===o||!(e&&!o||!e&&o)&&!objectsDiffer(e,o),equal_render_defs=(e,o)=>e===o||("boolean"!=typeof e&&"boolean"!=typeof o||e===o)&&e.$implicit===o.$implicit&&equal_variables(e.variables,o.variables),equal_render_configs_objectsDiffer=(e,o)=>{if(Object.keys(e).length!==Object.keys(o).length)return!0;for(const t of Object.keys(e))if(!equal_render_defs(e[t],o[t]))return!0;return!1},equal_render_configs=(e,o)=>!(!equal_render_defs(o,e)||"object"==typeof o&&"object"==typeof e&&equal_render_configs_objectsDiffer(o,e)),are_equal_config_params=(e,o)=>o===e||o.dependency===e.dependency&&o.export===e.export&&o.exportAll===e.exportAll&&!!equal_render_configs(e.render,o.render),are_equal_maps=(e,o,t=((e,o)=>e===o))=>{if(!o||o.size!==e.size)return!1;for(const r of(0,core_helpers.d4)(e)){if(!o.has(r))return!1;if(!t(o.get(r),e.get(r)))return!1}return!0},areEqualProviderDefs=(e,o,...t)=>{for(const r of t)if(o&&e&&o[r]&&e[r]&&o[r]===e[r])return!0;return o===e},are_equal_providers=(e,o)=>{if(Array.isArray(e)!==Array.isArray(o))return!1;const[t,r]=[(0,core_helpers.Bq)(e),(0,core_helpers.Bq)(o)];if(t.length!==r.length)return!1;for(let e=0;e{if(!o||o.size!==e.size)return!1;for(const t of(0,core_helpers.LG)(e))if(!o.has(t))return!1;return!0},get_empty_config=()=>({beforeCC:new Set,configDef:new Map,defProviders:new Map,defValue:new Map,excludeDef:new Set,keepDef:new Set,mockDef:new Set,providerDef:new Map,replaceDef:new Set}),required_metadata=e=>Object.assign(Object.assign({},e),{declarations:[...e.declarations||[]],imports:[...e.imports||[]],providers:[...e.providers||[]]});var mock_builder_performance_awaiter=function(e,o,t,r){return new(t||(t=Promise))((function(n,s){function c(e){try{i(r.next(e))}catch(e){s(e)}}function _(e){try{i(r.throw(e))}catch(e){s(e)}}function i(e){var o;e.done?n(e.value):(o=e.value,o instanceof t?o:new t((function(e){e(o)}))).then(c,_)}i((r=r.apply(e,o||[])).next())}))};class MockBuilderPerformance extends MockBuilderPromise{build(){const e=ng_mocks_universe.A.global;if(e.has("builder:module")&&e.has("builder:config")&&this.equalsTo(e.get("builder:config")))return required_metadata(e.get("builder:module"));e.has("builder:module")&&e.delete(e.get("builder:module"));const o=this.cloneConfig(),t=super.build();return e.set("builder:config",o),e.set("builder:module",t),required_metadata(t)}then(e,o){const t=Object.create(null,{then:{get:()=>super.then}});return mock_builder_performance_awaiter(this,void 0,void 0,(function*(){const r=ng_mocks_universe.A.global;if(r.has("bullet")&&r.has("builder:module")&&r.has("builder:config")&&this.equalsTo(r.get("builder:config")))return r.get(r.get("builder:module")).then(e,o);r.has("bullet")&&r.has("bullet:reset")&&(console.warn("ngMocks.faster has zero effect due to changes in testing module between runs"),r.delete("bullet"),testing_.TestBed.resetTestingModule(),r.set("bullet",!0));const n=t.then.call(this,e,o);return r.set(r.get("builder:module"),n),n}))}cloneConfig(){const e=get_empty_config();return(0,core_helpers.LG)(this.beforeCC,e.beforeCC),(0,core_helpers.LG)(this.excludeDef,e.excludeDef),(0,core_helpers.LG)(this.keepDef,e.keepDef),(0,core_helpers.LG)(this.mockDef,e.mockDef),(0,core_helpers.LG)(this.replaceDef,e.replaceDef),(0,core_helpers.Nn)(this.configDef,e.configDef),(0,core_helpers.Nn)(this.defProviders,e.defProviders),(0,core_helpers.Nn)(this.defValue,e.defValue),(0,core_helpers.Nn)(this.providerDef,e.providerDef),e}equalsTo(e){for(const o of["beforeCC","keepDef","replaceDef","excludeDef","mockDef"])if(!are_equal_sets(this[o],e[o]))return!1;for(const o of["defValue"])if(!are_equal_maps(this[o],e[o]))return!1;for(const o of["providerDef","defProviders"])if(!are_equal_maps(this[o],e[o],are_equal_providers))return!1;return are_equal_maps(this.configDef,e.configDef,are_equal_config_params)}}function MockBuilder(...e){const[o,t]=e,r=new MockBuilderPerformance(e.length<2?{export:!0}:{dependency:!0}),n=ng_mocks_universe.A.config.get("MockBuilderExtensions");for(const e of n?(0,core_helpers.d4)(n):[]){if((0,helper_extract_property_descriptor.A)(r,e))throw new Error(`MockBuilder.${e} is a base method and cannot be customized, please use a different name.`);(0,core_define_property.A)(r,e,((...o)=>(n.get(e)(r,o),r)))}if(o)for(const e of(0,core_helpers.Bq)(o))r.keep(e,{export:!0,shallow:isStandalone(e)});if(t)for(const e of(0,core_helpers.Bq)(t))r.mock(e,e,{export:!0,exportAll:!0});return r}function mockBuilderExtend(e,o){var t;const r=null!==(t=ng_mocks_universe.A.config.get("MockBuilderExtensions"))&&void 0!==t?t:new Map;o?(r.set(e,o),ng_mocks_universe.A.config.set("MockBuilderExtensions",r)):r.delete(e)}!function(e){e.extend=function(e,o){mockBuilderExtend(e,o)}}(MockBuilder||(MockBuilder={}));const is_debug_node=e=>!!(null==e?void 0:e.nativeElement)||!!(null==e?void 0:e.nativeNode),func_parse_find_args_name=e=>"string"==typeof e?e:"function"==typeof e?e.name:(0,func_is_ng_def.p)(e,"t")?e._desc:Array.isArray(e)?e[0]:e?"":"",is_fixture=e=>!!e&&"object"==typeof e&&void 0!==e.debugElement,findDebugElement=e=>is_fixture(e)?findDebugElement(e.debugElement):e&&e.injector&&e.query?e:void 0,func_parse_find_args=(e,o)=>{var t;let r,n,s=o;return 3===e.length?(r=findDebugElement(e[0]),n=e[1],s=e[2]):1===e.length?(r=findDebugElement(func_get_last_fixture()),[n]=e):e[0]?(r=findDebugElement(e[0]),r?n=e[1]:(r=findDebugElement(func_get_last_fixture()),[n,s]=e)):n=e[1],n=null!==(t=findDebugElement(n))&&void 0!==t?t:n,[r,n,s]};var platform_browser_x=e=>{var o={};return __webpack_require__.d(o,e),o},platform_browser_y=e=>()=>e;const platform_browser_namespaceObject=platform_browser_x({By:()=>__WEBPACK_EXTERNAL_MODULE__angular_platform_browser_bc6fa964__.By}),func_parse_find_term=e=>Array.isArray(e)?platform_browser_namespaceObject.By.css(1===e.length?`[${e[0]}]`:`[${e[0]}="${e[1]}"]`):"string"==typeof e?platform_browser_namespaceObject.By.css(e):platform_browser_namespaceObject.By.directive(getSourceOfMock(e)),defaultNotFoundValue={},mock_helper_find=(...e)=>{const[o,t,r]=func_parse_find_args(e,defaultNotFoundValue),n=is_debug_node(t)?t:null==o?void 0:o.query(func_parse_find_term(t));if(n)return n;if(r!==defaultNotFoundValue)return r;throw new Error(`Cannot find an element via ngMocks.find(${func_parse_find_args_name(t)})`)},detect_text_node=e=>"#text"===e.nativeNode.nodeName,el_def_compare=(e,o)=>!(!e||!o)&&e===o,el_def_get_node=e=>detect_text_node(e)?void 0:e.injector._tNode||e.injector.elDef||void 0,defaultInjector={},core_injector=(e,o=defaultInjector)=>{if(o===defaultInjector)return(0,core_helpers.d5)(e);try{return o.get(e)}catch(e){return}},getVcr=(e,o)=>{if(e!==o&&"#comment"===o.nativeNode.nodeName)return core_injector(core_.ViewContainerRef,o.injector)},getScanViewRefRootNodes=(e,o)=>{const t=getVcr(e,o);if(!t)return[];const r=[];for(let e=0;e{var o;let t,r;for(const n of(null===(o=e.parent)||void 0===o?void 0:o.childNodes)||[])for(const[o,s]of getScanViewRefRootNodes(e,n))s===e.nativeNode&&(void 0===r||o{var o,t,r,n;return(null===(o=e.injector._tNode)||void 0===o?void 0:o.parent)||(null===(t=e.injector.elDef)||void 0===t?void 0:t.parent)||scanViewRef(e)||(null===(r=e.parent)||void 0===r?void 0:r.injector._tNode)||(null===(n=e.parent)||void 0===n?void 0:n.injector.elDef)||void 0},nested_check_children=e=>{var o,t;const r=el_def_get_node(e);if(!r||detect_text_node(e))return[];const n=void 0!==e.childNodes,s=[];for(const t of e.childNodes||(null===(o=e.parent)||void 0===o?void 0:o.childNodes)||[]){const e=el_def_get_parent(t);(n||el_def_compare(r,e))&&(e&&!el_def_compare(r,e)||s.push(t))}if("BODY"===(null===(t=e.parent)||void 0===t?void 0:t.name)){const o=e.parent.childNodes;let t=o.length,r=0;for(let n=o.length-1;n>=0;n-=1){const s=o[n];if("#comment"===s.nativeNode.nodeName)r=n;else if(s.nativeNode===e.nativeNode){t=n+1;break}}for(let e=t;e{var t;if(o)return o;const r=el_def_get_parent(e),n=e.parent?el_def_get_node(e.parent):void 0;if(e.parent&&el_def_compare(r,n))return e.parent;for(const o of(null===(t=e.parent)||void 0===t?void 0:t.childNodes)||[]){const e=el_def_get_node(o);if(el_def_compare(r,e))return o}},nested_check_parent=detectParent,nestedCheck=(e,o,t,r=!1)=>{if(!e)return!1;if(!r&&detect_text_node(e))return!1;if(t(e,nested_check_parent(e,o)))return!0;for(const o of nested_check_children(e))if(nestedCheck(o,e,t,r))return!0;return!1},nested_check=nestedCheck,mock_helper_crawl=(e,o,t=!1)=>{const r=mock_helper_find(func_get_last_fixture(),e,void 0);nested_check(r,void 0,o,t)},isSelector=e=>("string"==typeof e||Array.isArray(e)&&"string"==typeof e[0]||is_fixture(e)||is_debug_node(e),!0),mock_helper_func_parse_find_args=(e,o,t)=>{let r,n,s=t;return 3===e.length?[r,n,s]=e:1===e.length?(r=func_get_last_fixture(),[n]=e):o(e[1])&&isSelector(e[0])?[r,n]=e:(r=func_get_last_fixture(),[n,s]=e),[r,n,s]},detect_attribute_in_selectors=(e,o)=>{for(const t of e){const e=t.match(/\[([^=\]]+)/g);if(e)for(const t of e)if(t===`[${o}`)return!0}return!1},getMeta=e=>{try{return core_reflect_directive_resolve(e)}catch(e){return}},func_parse_provider_tokens_directives=(e,o)=>{if(e)try{const t=(0,func_get_type.A)(o),r=core_injector(t,e.injector);return getMeta(r.constructor)}catch(e){return}},func_get_public_provider_keys=e=>e.injector.elDef?Object.keys(e.injector.elDef.element.publicProviders):[],func_parse_inputs_and_requires_attributes=(e,o)=>{const t=e.injector.elDef.element.publicProviders[o],r=t.provider.value;if(!r)return[[],[],0];const n=func_parse_provider_tokens_directives(e,r),s=t.bindings.map((e=>e.nonMinifiedName||e.name));return[(null==n?void 0:n.inputs)||[],s,t.nodeIndex]},collectSelectors=e=>{const o=[];for(const t of e.providerTokens){const r=func_parse_provider_tokens_directives(e,t);(null==r?void 0:r.selector)&&-1===o.indexOf(r.selector)&&o.push(r.selector)}return o},collectAttributesClassic=e=>{const o=[];for(const t of func_get_public_provider_keys(e)){const[r,n]=func_parse_inputs_and_requires_attributes(e,t);for(const e of r){const{name:t,alias:r}=(0,func_directive_io_parse.A)(e),s=r||t;-1!==n.indexOf(t)&&-1===o.indexOf(s)&&o.push(s)}}return o},collectAttributesIvy=e=>{var o,t;const r=[],n=(null===(o=e.injector._tNode)||void 0===o?void 0:o.attrs)||[];let s=2;for(let o=0;o[collectSelectors(e),[...collectAttributesClassic(e),...collectAttributesIvy(e)]],crawl_by_attribute=e=>o=>{const[t,r]=detect_selectors_from_node(o);return-1!==r.indexOf(e)||!!detect_attribute_in_selectors(t,e)},detectInClassic=(e,o,t)=>{for(const r of func_get_public_provider_keys(e)){const[n,s,c]=func_parse_inputs_and_requires_attributes(e,r);for(const r of n){const{name:n,alias:_}=(0,func_directive_io_parse.A)(r);if(o===(_||n)&&-1!==s.indexOf(n)&&t===e.injector.view.nodes[c].instance[n])return!0}}return!1},detectInIvy=(e,o,t)=>{var r,n,s;const c=(null===(r=e.injector._tNode)||void 0===r?void 0:r.attrs)||[];let _=2;for(let r=0;rt=>!!detectInIvy(t,e,o)||detectInClassic(t,e,o),crawl_by_declaration=e=>{const o=getSourceOfMock(e);return e=>!!e&&-1!==e.providerTokens.indexOf(o)&&void 0!==core_injector(o,e.injector)},crawl_by_id=e=>o=>!!o.references[e],regExp=new RegExp("\\[.*?\\]","g"),detect_tag_name_in_selectors=(e,o)=>{for(const t of e){const e=t.replace(regExp,"").split(",");for(const t of e)if(t.trim()===o)return!0}return!1},crawl_by_tag_name=e=>o=>{const[t]=detect_selectors_from_node(o);return detect_tag_name_in_selectors(t,e)},isCrawlByAttribute=e=>Array.isArray(e)&&1===e.length&&"string"==typeof e[0],isCrawlByAttributeValue=e=>Array.isArray(e)&&2===e.length&&"string"==typeof e[0],isCrawlById=e=>"string"==typeof e&&0===e.indexOf("#")&&e.length>1,isCrawlByTagName=e=>"string"==typeof e&&0!==e.indexOf("#")&&e.length>0,isCrawlByDeclaration=e=>"function"==typeof e,detect_crawler=e=>{if(isCrawlByAttribute(e))return crawl_by_attribute(e[0]);if(isCrawlByAttributeValue(e))return crawl_by_attribute_value(e[0],e[1]);if(isCrawlById(e))return crawl_by_id(e.slice(1));if(isCrawlByTagName(e))return crawl_by_tag_name(e);if(isCrawlByDeclaration(e))return crawl_by_declaration(e);throw new Error("Unknown selector")},func_is_valid_reveal_selector=e=>"string"==typeof e||!(!Array.isArray(e)||"string"!=typeof e[0])||"function"==typeof e,mock_helper_reveal_defaultNotFoundValue={},mock_helper_reveal=(...e)=>{const[o,t,r]=mock_helper_func_parse_find_args(e,func_is_valid_reveal_selector,mock_helper_reveal_defaultNotFoundValue),n=mock_helper_find(func_get_last_fixture(),o,void 0),s=detect_crawler(t);let c;if(mock_helper_crawl(n,(e=>!(e===n||detect_text_node(e)||!s(e)||(c=e,0)))),c)return c;if(r!==mock_helper_reveal_defaultNotFoundValue)return r;throw new Error(`Cannot find a DebugElement via ngMocks.reveal(${func_parse_find_args_name(t)})`)},mock_helper_reveal_all=(...e)=>{const[o,t]=mock_helper_func_parse_find_args(e,func_is_valid_reveal_selector),r=mock_helper_find(func_get_last_fixture(),o,void 0),n=detect_crawler(t),s=[];return mock_helper_crawl(r,(e=>{e!==r&&!detect_text_node(e)&&n(e)&&s.push(e)})),s},isMockControlValueAccessor=e=>!!func_is_mock(e)&&!!e.__ngMocksConfig.isControlValueAccessor;var helper_define_property_descriptor=__webpack_require__(6),helper_extract_methods_from_prototype=__webpack_require__(331);const is_html_element=e=>!!e&&"object"==typeof e&&void 0!==e.innerHTML,preventBubble=["focus","blur","load","unload","change","reset","scroll"],customEvent=(e,o)=>{const t=Object.assign({bubbles:!1,cancelable:!1},o),r=document.createEvent("CustomEvent");return r.initCustomEvent(e,t.bubbles,t.cancelable,null),r},eventCtor="function"==typeof Event?(e,o)=>new CustomEvent(e,o):customEvent,keyMap={alt:{altKey:!0,code:"AltLeft",key:"Alt",location:1,which:18},arrowdown:{code:"ArrowDown",key:"ArrowDown",location:0,which:40},arrowleft:{code:"ArrowLeft",key:"ArrowLeft",location:0,which:37},arrowright:{code:"ArrowRight",key:"ArrowRight",location:0,which:39},arrowup:{code:"ArrowUp",key:"ArrowUp",location:0,which:38},backspace:{code:"Backspace",key:"Backspace",location:0,which:8},control:{code:"ControlLeft",ctrlKey:!0,key:"Control",location:1,which:17},enter:{code:"Enter",key:"Enter",location:0,which:13},esc:{code:"Escape",key:"Escape",location:0,which:27},meta:{code:"MetaLeft",key:"Meta",location:1,metaKey:!0,which:91},shift:{code:"ShiftLeft",key:"Shift",location:1,shiftKey:!0,which:16},space:{code:"Space",key:" ",location:0,which:32},tab:{code:"Tab",key:"Tab",location:0,which:9}};for(let e=1;e<=12;e+=1)keyMap[`f${e}`]={code:`F${e}`,key:`F${e}`,location:0,which:e+111};const getCode=e=>{const o=e.codePointAt(0);return o&&o>=97&&o<=122||o&&o>=65&&o<=90?`Key${e.toUpperCase()}`:o&&o>=48&&o<=57?`Digit${e}`:"Unknown"},applyPayload=(e,o)=>{const t={};for(const e of o?o.split("."):[]){let o=keyMap[e];if(o||1!==e.length||(o={code:getCode(e),key:e}),!o)throw new Error(`Unknown event part ${e}`);(0,mock_helper_stub.A)(t,o)}o&&(0,mock_helper_stub.A)(e,t)},mock_helper_event=(e,o,t)=>{const r=e.indexOf("."),[n,s]=-1===r?[e]:[e.slice(0,Math.max(0,r)),e.slice(r+1)],c=eventCtor(n,Object.assign({bubbles:-1===preventBubble.indexOf(e),cancelable:!0},o));return applyPayload(c,s),t&&(0,mock_helper_stub.A)(c,t),c},mock_helper_trigger_preventBubble=["focus","blur","load","unload","change","reset","scroll"],toEventObj=e=>"string"==typeof e?mock_helper_event(e,{bubbles:-1===mock_helper_trigger_preventBubble.indexOf(e),cancelable:!0}):e,getNativeElement=e=>is_debug_node(e)||is_fixture(e)?e.nativeElement:is_html_element(e)?e:void 0,mock_helper_trigger=(e,o,t)=>{const r=is_html_element(e)?e:mock_helper_find(func_get_last_fixture(),e,void 0),n=getNativeElement(r);if(!n)throw new Error(`Cannot trigger ${"string"==typeof o?o:o.type} event undefined element`);if(n.disabled)return;const s=toEventObj(o);s.target||(0,mock_helper_stub.A)(s,{target:n}),t&&(0,mock_helper_stub.A)(s,t),n.dispatchEvent(s)};var mock_helper_stub_member=__webpack_require__(589);const message=["Cannot find ControlValueAccessor on the element.","If it is a mock input with [formControlName],","you need either to avoid mocking ReactiveFormsModule","or to avoid accessing the control in such a way,","because this tests ReactiveFormsModule instead of own implementation."].join(" "),func_get_vca=e=>{const o=core_form&&core_injector(core_form.NgControl,e.injector),t=null==o?void 0:o.valueAccessor;if(t)return t;const r=core_form&&core_injector(core_form.FormControlDirective,e.injector);if(null==r?void 0:r.form)return r.form;const n=core_form&&core_injector(core_form.NgModel,e.injector);if(n)return n;throw new Error(message)},triggerInput=(e,o)=>{mock_helper_trigger(e,"focus");const t=Object.getOwnPropertyDescriptor(e.nativeElement,"value");(0,mock_helper_stub_member.A)(e.nativeElement,"value",o),mock_helper_trigger(e,"input"),mock_helper_trigger(e,"change"),t&&((0,helper_define_property_descriptor.A)(e.nativeElement,"value",t),e.nativeElement.value=o),mock_helper_trigger(e,"blur")},handleKnown=(e,o)=>core_form&&e instanceof core_form.AbstractControl?(e.setValue(o),!0):core_form&&e instanceof core_form.NgModel?(e.update.emit(o),!0):!!isMockControlValueAccessor(e.instance)&&(e.instance.__simulateChange(o),!0),hasListener=e=>e.listeners.some((e=>"input"===e.name||"change"===e.name)),keys=["onChange","onChangeCallback","onChangeCb","onChangeClb","onChangeFn","_onChange","_onChangeCallback","_onChangeCb","_onChangeClb","_onChangeFn","changeFn","_changeFn","onModelChange","cvaOnChange","cvaOnChangeCallback","cvaOnChangeCb","cvaOnChangeClb","cvaOnChangeFn","_cvaOnChange","_cvaOnChangeCallback","_cvaOnChangeCb","_cvaOnChangeClb","_cvaOnChangeFn"],mock_helper_change=(e,o,t)=>{const r=mock_helper_find(func_get_last_fixture(),e,void 0);if(!r)throw new Error(`Cannot find an element via ngMocks.change(${func_parse_find_args_name(e)})`);const n=func_get_vca(r);if(handleKnown(n,o)||hasListener(r))return void triggerInput(r,o);for(const e of t?[t]:keys)if("function"==typeof n[e])return n.writeValue(o),void n[e](o);const s=(0,helper_extract_methods_from_prototype.A)(n);throw new Error(["Unsupported type of ControlValueAccessor,",`please ensure it has '${t||"onChange"}' method.`,"If it is a 3rd-party library, please provide the correct name of the method in the 'methodName' parameter.","Possible Names: "+s.join(", ")+"."].join(" "))},triggerTouch=e=>{mock_helper_trigger(e,"focus"),mock_helper_trigger(e,"blur")},mock_helper_touch_handleKnown=e=>core_form&&e instanceof core_form.AbstractControl?(e.markAsTouched(),!0):!!isMockControlValueAccessor(e.instance)&&(e.instance.__simulateTouch(),!0),mock_helper_touch_hasListener=e=>e.listeners.some((e=>"focus"===e.name||"blur"===e.name)),mock_helper_touch_keys=["onTouched","onTouchedCallback","onTouchedCb","onTouchedClb","onTouchedFn","_onTouched","_onTouchedCallback","_onTouchedCb","_onTouchedClb","_onTouchedFn","markAsTouched","_markAsTouched","onModelTouched","cvaOnTouch","cvaOnTouchCallback","cvaOnTouchCb","cvaOnTouchClb","cvaOnTouchFn","_cvaOnTouch","_cvaOnTouchCallback","_cvaOnTouchCb","_cvaOnTouchClb","_cvaOnTouchFn"],mock_helper_touch=(e,o)=>{const t=mock_helper_find(func_get_last_fixture(),e,void 0);if(!t)throw new Error(`Cannot find an element via ngMocks.touch(${func_parse_find_args_name(e)})`);const r=func_get_vca(t);if(mock_helper_touch_handleKnown(r)||mock_helper_touch_hasListener(t))return void triggerTouch(t);for(const e of o?[o]:mock_helper_touch_keys)if("function"==typeof r[e])return void r[e]();const n=(0,helper_extract_methods_from_prototype.A)(r);throw new Error(["Unsupported type of ControlValueAccessor,",`please ensure it has '${o||"onTouched"}' method.`,"If it is a 3rd-party library, please provide the correct name of the method in the 'methodName' parameter.","Possible Names: "+n.join(", ")+"."].join(" "))},mock_helper_click=(e,o)=>{mock_helper_trigger(e,"click",o)},mock_helper_find_all=(...e)=>{const[o,t]=func_parse_find_args(e);return is_debug_node(t)?[t]:(null==o?void 0:o.queryAll(func_parse_find_term(t)))||[]},getParentWithInjector=e=>{let o=e;for(;"NullInjector"===(null==o?void 0:o.injector.constructor.name);)o=o.parent;if(o)return o.injector},func_get_from_node_injector=(e,o,t)=>{if(!o.injector||"NullInjector"===o.injector.constructor.name)return;const r=getParentWithInjector(o.parent),n=r?core_injector(t,r):void 0,s=core_injector(t,o.injector);n!==s&&((0,func_is_ng_def.p)(t,"t")&&void 0!==s||void 0!==s&&-1===e.indexOf(s))&&e.push(s)},func_get_from_node_element=e=>{var o;return"#text"===(null===(o=e.nativeNode)||void 0===o?void 0:o.nodeName)&&e.parent?e.parent:e},detectGatherFlag=(e,o,t)=>!!(o&&o.nativeNode&&"#comment"===o.nativeNode.nodeName&&Array.isArray(t)&&t[0]===o.nativeNode)||!Array.isArray(t)&&(o&&t.nodeName?"#comment"===t.nodeName?t===o.nativeNode:"#text"===t.nodeName&&t.parentNode===o.nativeNode:e),isNotObject=e=>!e||"object"!=typeof e,shouldBeScanned=(e,o)=>-1===e.indexOf(o)&&Array.isArray(o),scan=({result:e,el:o,nodes:t,normalize:r,proto:n},s,c=[])=>{c.push(t);let _=s,i=t.length;t.length>1&&t[1]&&"object"==typeof t[1]&&t[1].bindingStartIndex&&(i=t[1].bindingStartIndex);for(let s=0;s{if("object"==typeof e[1]&&e[20]===o)return e;for(let t=21;t{var o;let t=e,r=null===(o=t.nativeNode)||void 0===o?void 0:o.__ngContext__;for(;void 0===r&&t.parent;)t=t.parent,r=t.nativeNode.__ngContext__;if("number"!=typeof r)return r;const n=t.injector._lView;return Array.isArray(n)?detectContextByIndex(n,r):void 0},contextToNodes=e=>Array.isArray(e)?e:null==e?void 0:e.lView,func_get_from_node_ivy=(e,o,t)=>{if(!o||o._debugContext)return;const r=func_get_from_node_element(o);func_get_from_node_scan({el:r,nodes:contextToNodes(detectContext(o))||[],normalize:e=>e,proto:t,result:e},!0)},normalize=e=>{if(!e||"object"!=typeof e)return e;for(const o of["renderElement","renderText","instance"])if(e[o])return e[o];return null},func_get_from_node_standard=(e,o,t)=>{if(!o||!o._debugContext)return;const r=func_get_from_node_element(o);func_get_from_node_scan({el:r,nodes:o._debugContext.view.nodes,normalize,proto:t,result:e},!0)},func_get_from_node=(e,o,t)=>(func_get_from_node_injector(e,o,t),(0,func_is_ng_def.p)(t,"t")||"string"==typeof t||(func_get_from_node_standard(e,o,t),func_get_from_node_ivy(e,o,t)),e),func_is_valid_find_instance_selector=e=>"function"==typeof e||(0,func_is_ng_def.p)(e,"t"),mock_helper_find_instance_defaultNotFoundValue={},mock_helper_find_instance=(...e)=>{const[o,t,r]=mock_helper_func_parse_find_args(e,func_is_valid_find_instance_selector,mock_helper_find_instance_defaultNotFoundValue);if("function"!=typeof t&&!(0,func_is_ng_def.p)(t,"t")&&"string"!=typeof t)throw new Error("Only classes or tokens are accepted");const n=getSourceOfMock(t),s=[],c=func_get_last_fixture();if(c)mock_helper_crawl(mock_helper_find(c,o,void 0),((e,o)=>(func_get_from_node(s,e,n),0===s.length&&o&&"#comment"===o.nativeNode.nodeName&&func_get_from_node(s,o,n),s.length>0)),!0);else try{s.push((0,core_helpers.Ah)(n))}catch(e){if(!e||"object"!=typeof e||void 0===e.ngTempTokenPath)throw e}if(s.length>0)return s[0];if(r!==mock_helper_find_instance_defaultNotFoundValue)return r;throw new Error(`Cannot find an instance via ngMocks.findInstance(${func_parse_find_args_name(t)})`)},mock_helper_find_instances=(...e)=>{const[o,t]=mock_helper_func_parse_find_args(e,func_is_valid_find_instance_selector);if("function"!=typeof t&&!(0,func_is_ng_def.p)(t,"t")&&"string"!=typeof t)throw new Error("Only classes or tokens are accepted");const r=getSourceOfMock(t),n=[],s=[],c=func_get_last_fixture();if(c){const e=mock_helper_find_all(c,o,void 0);for(const o of e)mock_helper_crawl(o,((e,o)=>{-1===s.indexOf(e)&&(func_get_from_node(n,e,r),s.push(e)),o&&"#comment"===o.nativeNode.nodeName&&-1===s.indexOf(o)&&(func_get_from_node(n,o,r),s.push(o))}),!0)}else try{n.push((0,core_helpers.Ah)(r))}catch(e){}return n},handle_array=(e,o)=>e(o.map((o=>e(o,!0))).join("")),format_handler=e=>(o,t=!1)=>{const r=(o,n=!1)=>{if(Array.isArray(o))return handle_array(r,o);if(is_fixture(o))return r(o.debugElement,t);const s=e(r,o,n);return void 0!==s?s:is_debug_node(o)&&"#comment"===o.nativeNode.nodeName?r(nested_check_children(o),!0):is_debug_node(o)?r(o.nativeNode,n):""};return Array.isArray(o)?o.map((e=>r(e,t))):r(o,t)},handle_text=e=>{var o,t;return null!==(t=null!==(o=e.nodeValue)&&void 0!==o?o:e.textContent)&&void 0!==t?t:e.wholeText},is_text=e=>!!e&&"object"==typeof e&&"#text"===e.nodeName,normalizeValue=e=>e?e.replace(new RegExp("\\s+","mg")," ").replace(new RegExp("\x3c!--(.|\\n|\\r)*?--\x3e|\x3c!--(.|\\n|\\r)*","mg"),"").replace(new RegExp("\\s+","mg")," ").replace(new RegExp(">\\s+<","mg"),"><").replace(new RegExp('"\\s+>',"mg"),'">'):"",normalizeText=e=>e.replace(new RegExp("&","mg"),"&").replace(new RegExp('"',"mg"),""").replace(new RegExp("<","mg"),"<").replace(new RegExp(">","mg"),">").replace(new RegExp("'","mg"),"'"),getElementValue=(e,o)=>o?e.outerHTML:e.innerHTML,handlePrimitives=(e,o,t)=>{if("string"==typeof o||void 0===o){const e=normalizeValue(o);return t?e:e.trim()}return is_html_element(o)?e(getElementValue(o,t)):is_text(o)?handlePrimitives(e,normalizeText(handle_text(o)),t):void 0},mock_helper_format_html=format_handler(handlePrimitives),mock_helper_format_text_normalizeValue=e=>e?e.replace(new RegExp("\\s+","mg")," "):"",mock_helper_format_text_getElementValue=(e,o)=>{var t;const r=null!==(t=e.textContent)&&void 0!==t?t:"";return o?r:r.trim()},mock_helper_format_text_handlePrimitives=(e,o,t)=>{if("string"==typeof o||void 0===o){const e=mock_helper_format_text_normalizeValue(o);return t?e:e.trim()}return is_html_element(o)?e(mock_helper_format_text_getElementValue(o,t)):is_text(o)?mock_helper_format_text_handlePrimitives(e,handle_text(o),t):void 0},mock_helper_format_text=format_handler(mock_helper_format_text_handlePrimitives),calls=[],mock_helper_auto_spy=e=>{"reset"===e?calls.pop():calls.push(e);const o=calls[calls.length-1];return"jasmine"===o?(0,helper_mock_service.O)((e=>jasmine.createSpy(e))):"jest"===o?(0,helper_mock_service.O)((e=>jest.fn().mockName(e))):o&&"default"!==o&&"reset"!==o?(0,helper_mock_service.O)(o):(0,helper_mock_service.O)()},mock_helper_console=(e,o)=>(...t)=>{const r=[];beforeEach((()=>{for(const o of e)-1===t.indexOf(o)&&t.push(o);for(const e of t)(0,core_define_property.A)(console,`__ngMocksBackup_${e}`,console[`__ngMocksBackup_${e}`]||[]),console[`__ngMocksBackup_${e}`].push(console[e]),r.push(e),console[e]=o(e)})),afterEach((()=>{for(const e of r)console[e]=console[`__ngMocksBackup_${e}`].pop();r.splice(0,r.length)}))},factory=e=>helper_mock_service.A.mockFunction(`console.${e}`),mock_helper_console_ignore=mock_helper_console(["log"],factory),mock_helper_console_throw_factory=e=>(...o)=>{const t=new Error(o.join(" "));throw(0,core_define_property.A)(t,"ngMocksConsoleCatch",e),t},mock_helper_console_throw=mock_helper_console(["warn","error"],mock_helper_console_throw_factory),mock_helper_default_config=(e,o)=>{const t=ng_mocks_universe.A.getConfigMock();for(const r of(0,core_helpers.Bq)(e))o?t.set(r,o):t.delete(r)},mock_helper_default_mock=(e,o)=>{const t=ng_mocks_universe.A.getOverrides();for(const r of(0,core_helpers.Bq)(e))if(o){const e=t.has(r)?t.get(r):new Set;e.add(o),t.set(r,e)}else t.delete(r)},hooks=ng_mocks_universe.A.global.get("faster-hooks")||{after:[],before:[]};ng_mocks_universe.A.global.set("faster-hooks",hooks);const configureTestingModule=(e,o)=>t=>{if(testing_.TestBed.ngMocksFasterLock)return e.call(o,t);ng_mocks_universe.A.global.set("bullet:customized",!0);let r=e;for(const e of hooks.before)r=e(r,o);try{return(0,core_define_property.A)(testing_.TestBed,"ngMocksFasterLock",!0),r.call(o,t)}finally{(0,core_define_property.A)(testing_.TestBed,"ngMocksFasterLock",void 0)}},resetTestingModule=(e,o)=>()=>{if(testing_.TestBed.ngMocksFasterLock)return e.call(o);if(ng_mocks_universe.A.global.has("bullet"))return ng_mocks_universe.A.global.has("bullet:customized")&&ng_mocks_universe.A.global.set("bullet:reset",!0),o;ng_mocks_universe.A.global.delete("bullet:customized"),ng_mocks_universe.A.global.delete("bullet:reset");let t=e;for(const e of hooks.after)t=e(t,o);try{return(0,core_define_property.A)(testing_.TestBed,"ngMocksFasterLock",!0),t.call(o)}finally{(0,core_define_property.A)(testing_.TestBed,"ngMocksFasterLock",void 0)}},mock_helper_faster_install=()=>{testing_.TestBed.ngMocksFasterInstalled||(testing_.TestBed.configureTestingModule=configureTestingModule(testing_.TestBed.configureTestingModule,testing_.TestBed),testing_.TestBed.resetTestingModule=resetTestingModule(testing_.TestBed.resetTestingModule,testing_.TestBed),(0,core_define_property.A)(testing_.TestBed,"ngMocksFasterInstalled",!0));const e=(0,testing_.getTestBed)();return e.ngMocksFasterInstalled||(e.configureTestingModule=configureTestingModule(e.configureTestingModule,e),e.resetTestingModule=resetTestingModule(e.resetTestingModule,e),(0,core_define_property.A)(e,"ngMocksFasterInstalled",!0)),hooks},mock_helper_flush_test_bed=()=>{const e=(0,testing_.getTestBed)();e._instantiated=!1,e._moduleFactory=void 0,e._testModuleRef=null},resetFixtures=e=>{const o=(0,testing_.getTestBed)()._activeFixtures||[];let t=0;for(let r=o.length-1;r>=0;r-=1)o[r].ngMocksStackId&&o[r].ngMocksStackId!==e?t+=1:(o[r].ngMocksStackId=void 0,o[r].destroy(),o.splice(r,1));0===t&&mock_helper_flush_test_bed()},idAdd=e=>{var o;const t=null!==(o=ng_mocks_universe.A.global.get("bullet:stack"))&&void 0!==o?o:[];t.push(e),ng_mocks_universe.A.global.set("bullet:stack",t),ng_mocks_universe.A.global.set("bullet:stack:id",e)},idRemove=e=>{const o=ng_mocks_universe.A.global.get("bullet:stack");o.splice(o.indexOf(e),1),o.length>0?ng_mocks_universe.A.global.set("bullet:stack:id",o[o.length-1]):ng_mocks_universe.A.global.delete("bullet:stack:id"),resetFixtures(e)},mock_helper_faster=()=>{mock_helper_faster_install();const e={},o={};beforeAll((()=>{ng_mocks_universe.A.global.has("bullet:customized")&&testing_.TestBed.resetTestingModule(),ng_mocks_universe.A.global.set("bullet",!0),idAdd(e)})),beforeEach((()=>{idAdd(o)})),afterEach((()=>{idRemove(o)})),afterAll((()=>{idRemove(e),ng_mocks_universe.A.global.delete("bullet"),ng_mocks_universe.A.global.has("bullet:reset")&&testing_.TestBed.resetTestingModule()}))},mock_helper_get_defaultNotFoundValue={},parseArgs=e=>({el:e[0],notFoundValue:3===e.length?e[2]:mock_helper_get_defaultNotFoundValue,sel:e[1]}),mock_helper_get=(...e)=>{if(1===e.length)try{return testing_.TestBed.inject?testing_.TestBed.inject(e[0]):testing_.TestBed.get(e[0])}catch(o){if(!o||"object"!=typeof o||void 0===o.ngTempTokenPath)throw o;throw new Error(`Cannot find an instance via ngMocks.get(${func_parse_find_args_name(e[0])})`)}const{el:o,sel:t,notFoundValue:r}=parseArgs(e),n=mock_helper_find(func_get_last_fixture(),o,void 0),s=getSourceOfMock(t);if(n){const e=func_get_from_node([],n,s);if(e.length>0)return e[0]}if(n){const e=nested_check_parent(n,void 0);if(e&&"#comment"===e.nativeNode.nodeName){const o=func_get_from_node([],e,s);if(o.length>0)return o[0]}}if(r!==mock_helper_get_defaultNotFoundValue)return r;throw new Error(`Cannot find ${(0,func_get_name.A)(t)} instance via ngMocks.get`)},iterator=(e,o,t=new Set)=>{const r=(0,collect_declarations.A)(e);for(const e of r.decorators)for(const n of core_config.A.dependencies)if(r[e][n])for(const s of(0,core_helpers.Bq)(r[e][n])){const e=(0,func_get_type.A)(s);e&&!t.has(e)&&(t.add(e),o(e),iterator(e,o,t))}},func_iterate_declaration=iterator,func_global_prepare=()=>{var e;ng_mocks_universe.A.cacheDeclarations.clear(),null===(e=ng_mocks_universe.A.config.get("ngMocksDepsSkip"))||void 0===e||e.clear()},action=e=>{ng_mocks_universe.A.getDefaults().set(e,["exclude"])},mock_helper_global_exclude=(e,o=!1)=>{func_global_prepare(),action(e),o&&func_iterate_declaration(e,action)},mock_helper_global_keep_action=e=>{ng_mocks_universe.A.getDefaults().set(e,["keep"])},mock_helper_global_keep=(e,o=!1)=>{func_global_prepare(),mock_helper_global_keep_action(e),o&&func_iterate_declaration(e,mock_helper_global_keep_action)},mock_helper_global_mock_action=e=>{ng_mocks_universe.A.getDefaults().set(e,["mock"])},mock_helper_global_mock=(e,o=!1)=>{func_global_prepare(),mock_helper_global_mock_action(e),o&&func_iterate_declaration(e,mock_helper_global_mock_action)},mock_helper_global_replace=(e,o)=>{let t=!0;if(((0,func_is_ng_def.p)(e,"m")&&(0,func_is_ng_def.p)(o,"m")||(0,func_is_ng_def.p)(e,"c")&&(0,func_is_ng_def.p)(o,"c")||(0,func_is_ng_def.p)(e,"d")&&(0,func_is_ng_def.p)(o,"d")||(0,func_is_ng_def.p)(e,"p")&&(0,func_is_ng_def.p)(o,"p"))&&(t=!1),t)throw new Error("Cannot replace the declaration, both have to be a Module, a Component, a Directive or a Pipe");func_global_prepare(),ng_mocks_universe.A.getDefaults().set(e,["replace",o])},mock_helper_global_wipe_action=e=>{ng_mocks_universe.A.getDefaults().delete(e),mock_helper_default_mock(e)},mock_helper_global_wipe=(e,o=!1)=>{func_global_prepare(),mock_helper_global_wipe_action(e),o&&func_iterate_declaration(e,mock_helper_global_wipe_action)},mock_helper_guts_skipDef=(e,o,t)=>!!o.has(e)||(o.add(e),t.has(e)),createMetaHandler=(e,o,t,r,n)=>{const s=e.get(o)||o;(0,func_is_ng_def.p)(s,"m")?t.push(s):(0,func_is_ng_def.p)(s,"c")||(0,func_is_ng_def.p)(s,"d")?r.push(s):(0,func_is_ng_def.p)(s,"p")?(r.push(s),n.push(s)):(0,func_is_ng_injection_token.S)(s)||n.push(s)},createMeta=({keep:e,skip:o,optional:t,exclude:r,imports:n,declarations:s,providers:c})=>{for(const _ of e)o.has(_)||r.has(_)||t.has(_)||createMetaHandler(t,_,n,s,c);return{declarations:s,imports:n,providers:c}},typeMap=[["m","module"],["c","component"],["d","directive"],["p","pipe"]],mock_helper_guts_getType=(e,o)=>{if((0,func_is_ng_module_def_with_providers.h)(e))return"module-with-providers";for(const[t,r]of typeMap)if((0,func_is_ng_def.p)(e,t))return"m"===t&&o.has(e)?`${r}-keep`:r;return""},handleModuleWithProviders=(e,o)=>{e.skip.has(o.ngModule)||(e.skip.add(o.ngModule),e.exclude.has(o.ngModule)||e.imports.push(e.keep.has(o.ngModule)?o:MockModule(o)))},handleDeclaration=(e,o,t,r)=>{mock_helper_guts_skipDef(o,e.skip,e.exclude)||r.push(e.keep.has(o)?o:t(o))},handleDestructuring=(e,o,t)=>{if(mock_helper_guts_skipDef(o,e.skip,e.exclude))return;const r=core_reflect_module_resolve(o);for(const o of(0,core_helpers.Bq)([r.declarations,r.imports]))t(e,o);for(const o of r.providers?(0,core_helpers.Bq)(r.providers):[])resolveProvider(e,o)},resolveProvider=({skip:e,keep:o,providers:t,exclude:r},n)=>{const s=(0,func_get_type.A)(n);if(e.add(s),r.has(s))return;const c=o.has(s)?n:(0,mock_provider.A)(n);c&&t.push(c)},resolveMap={component:MockComponent,directive:MockDirective,pipe:MockPipe},resolveHandler=(e,o,t,r)=>{"module-with-providers"===o?handleModuleWithProviders(e,t):"module-keep"===o||"module"===o&&r?handleDeclaration(e,t,MockModule,e.imports):"module"===o?handleDestructuring(e,t,resolve):resolveMap[o]?handleDeclaration(e,t,resolveMap[o],e.declarations):resolveProvider(e,t)},resolve=(e,o,t=!0)=>{if(!o)return;const r=mock_helper_guts_getType(o,e.keep);let n;if("module-with-providers"!==r){const t=e.optional.get(o);t&&t!==o&&(n=t,e.keep.add(n))}n||(n=o),resolveHandler(e,r,n,t)},generateDataWithUniverse=(e,o,t,r)=>{for(const n of(0,core_helpers.d4)(ng_mocks_universe.A.getDefaults())){const s=ng_mocks_universe.A.getBuildDeclaration(n);e.has(n)||o.has(n)||t.has(n)||(r.set(n,s),null===s?t.add(n):void 0===s?o.add(n):n===s&&e.add(n))}},generateData=(e,o,t)=>{const r=new Set((0,core_helpers.Bq)(e||[])),n=new Set((0,core_helpers.Bq)(o||[])),s=new Set((0,core_helpers.Bq)(t||[])),c=new Map;return generateDataWithUniverse(r,n,s,c),{declarations:[],exclude:s,imports:[],keep:r,mock:n,optional:c,providers:[],skip:new Set}},mock_helper_guts=(e,o=null,t=null)=>{const r=generateData(e,o,t),n=new Map;ng_mocks_universe.A.config.set("ngMocksDepsResolution",n);for(const e of(0,core_helpers.LG)(r.keep))n.set(e,"keep");for(const e of(0,core_helpers.LG)(r.exclude))n.set(e,"exclude");ng_mocks_universe.A.config.set("mockNgDefResolver",new core_def_stack);for(const e of(0,core_helpers.LG)(r.mock))n.set(e,"mock"),r.optional.has(e)||resolve(r,e,!1);const s=createMeta(r);return ng_mocks_universe.A.config.delete("mockNgDefResolver"),ng_mocks_universe.A.config.delete("ngMocksDepsResolution"),s},mock_helper_attributes_defaultNotFoundValue={},mock_helper_attributes_parseArgs=e=>[e[0],e[1],3===e.length?e[2]:mock_helper_attributes_defaultNotFoundValue],attrMatches=(e,o)=>{const{name:t,alias:r=""}=(0,func_directive_io_parse.A)(e);if(!r&&t===o||r&&r===o)return t},detectAttribute=(e,o,t)=>{for(const r of(null==e?void 0:e.providerTokens)||[]){const n=func_parse_provider_tokens_directives(e,r);if(n)for(const s of n[o]||[]){const o=attrMatches(s,t);if(o)return mock_helper_get(e,r)[o]}}throw new Error("Not found")},mock_helper_attributes=(e,o,...t)=>{const[r,n,s]=mock_helper_attributes_parseArgs(t);try{return detectAttribute(mock_helper_find(func_get_last_fixture(),r,void 0),o,n)}catch(e){}if(s!==mock_helper_attributes_defaultNotFoundValue)return s;throw new Error(`Cannot find ${n} ${e} via ngMocks.${e}`)},mock_helper_input=(...e)=>mock_helper_attributes("input","inputs",...e),mock_helper_output=(...e)=>mock_helper_attributes("output","outputs",...e),mock_helper_reset=()=>{ng_mocks_universe.A.builtDeclarations=new Map,ng_mocks_universe.A.builtProviders=new Map,ng_mocks_universe.A.cacheDeclarations=new Map,ng_mocks_universe.A.cacheProviders=new Map,ng_mocks_universe.A.config=new Map,ng_mocks_universe.A.configInstance=new Map,ng_mocks_universe.A.flags=new Set(core_config.A.flags),ng_mocks_universe.A.touches=new Set},getValVcr=e=>{const o=[];for(const t of e.__ngMocksConfig.queryScanKeys||[]){const r=e[t],n=e[`__ngMocksVcr_${t}`],s=r instanceof core_.QueryList?r.toArray():[r],c=n instanceof core_.QueryList?n.toArray():[n];for(let e=0;e!!e.__template&&!!e.__vcr&&o(e.__template)&&t(e.__vcr,e.__template),isRightTemplate=(e,o,t)=>!!e&&o instanceof core_.TemplateRef&&t(o),findDeep=(e,o,t)=>{if(!func_is_mock(e))throw new Error("Only instances of mock declarations are accepted");if(handleDirective(e,o,t))return!0;for(const[r,n]of getValVcr(e)){if(func_is_mock(r)&&findDeep(r,o,t))return!0;if(isRightTemplate(n,r,o))return t(n,r)}return!1},func_find_deep=findDeep,func_parse_template=e=>{if(e instanceof core_.TemplateRef)return e;if(func_is_mock(e)&&e.__template)return e.__template;const o=(null==e?void 0:e.nativeNode)&&e.injector;if(o){const e=core_injector(core_.TemplateRef,o);if(e)return e}const t=new Error("Unknown template has been passed, only TemplateRef or a mock structural directive are supported");throw t.param=e,t},mock_helper_hide=(e,o)=>{const t=o?func_parse_template(o):void 0;let r=!1;if(func_find_deep(e,(e=>!t||e.elementRef.nativeElement===t.elementRef.nativeElement),(e=>(e.clear(),r=!0,!1))),!r)throw new Error("Cannot find path to the TemplateRef")},mock_helper_render=(e,o,t,r)=>{const n=func_parse_template(o);if(!func_find_deep(e,(e=>e.elementRef.nativeElement===n.elementRef.nativeElement),((e,o)=>{const n=Object.assign(Object.assign({},r),{$implicit:t});return e.clear(),e.createEmbeddedView(o,n).detectChanges(),!0})))throw new Error("Cannot find path to the TemplateRef")},template_ref_detect_crawler=e=>{if("string"==typeof e)return crawl_by_id(e);if(Array.isArray(e)&&1===e.length&&"string"==typeof e[0])return crawl_by_attribute(e[0]);if(Array.isArray(e)&&2===e.length&&"string"==typeof e[0])return crawl_by_attribute_value(e[0],e[1]);if("function"==typeof e)return crawl_by_declaration(e);throw new Error("Unknown selector")},detect_template_ref=(e,o,t=0)=>r=>{try{const t=!detect_text_node(r)&&o(r)?core_injector(core_.TemplateRef,r.injector):void 0;t&&e.push(t)}catch(e){}return!!t&&e.length===t},func_is_valid_template_ref_selector=e=>"string"==typeof e||!(!Array.isArray(e)||"string"!=typeof e[0])||"function"==typeof e,mock_helper_find_template_ref_defaultNotFoundValue={},mock_helper_find_template_ref=(...e)=>{const[o,t,r]=mock_helper_func_parse_find_args(e,func_is_valid_template_ref_selector,mock_helper_find_template_ref_defaultNotFoundValue),n=[],s=template_ref_detect_crawler(t);if(nested_check(mock_helper_find(func_get_last_fixture(),o,void 0),void 0,detect_template_ref(n,s,1)),n.length>0)return n[0];if(r!==mock_helper_find_template_ref_defaultNotFoundValue)return r;throw new Error(`Cannot find a TemplateRef via ngMocks.findTemplateRef(${func_parse_find_args_name(t)})`)},mock_helper_find_template_refs=(...e)=>{const[o,t]=mock_helper_func_parse_find_args(e,func_is_valid_template_ref_selector),r=[],n=template_ref_detect_crawler(t);return nested_check(mock_helper_find(func_get_last_fixture(),o,void 0),void 0,detect_template_ref(r,n)),r},flagNames=["onMockBuilderMissingDependency","onMockInstanceRestoreNeed","onTestBedFlushNeed"],mock_helper_object={autoSpy:mock_helper_auto_spy,change:mock_helper_change,click:mock_helper_click,config:e=>{const o=ng_mocks_universe.A.global.get("flags");for(const t of flagNames)null===e[t]?o[t]=core_config.A[t]:void 0!==e[t]&&(o[t]=e[t]);null===e.mockRenderCacheSize?ng_mocks_universe.A.global.delete("mockRenderCacheSize"):void 0!==e.mockRenderCacheSize&&ng_mocks_universe.A.global.set("mockRenderCacheSize",e.mockRenderCacheSize)},crawl:mock_helper_crawl,defaultConfig:mock_helper_default_config,defaultMock:mock_helper_default_mock,event:mock_helper_event,faster:mock_helper_faster,find:mock_helper_find,findAll:mock_helper_find_all,findInstance:mock_helper_find_instance,findInstances:mock_helper_find_instances,findTemplateRef:mock_helper_find_template_ref,findTemplateRefs:mock_helper_find_template_refs,flushTestBed:mock_helper_flush_test_bed,formatHtml:mock_helper_format_html,formatText:mock_helper_format_text,get:mock_helper_get,globalExclude:mock_helper_global_exclude,globalKeep:mock_helper_global_keep,globalMock:mock_helper_global_mock,globalReplace:mock_helper_global_replace,globalWipe:mock_helper_global_wipe,guts:mock_helper_guts,hide:mock_helper_hide,ignoreOnConsole:mock_helper_console_ignore,input:mock_helper_input,output:mock_helper_output,render:mock_helper_render,reset:mock_helper_reset,reveal:mock_helper_reveal,revealAll:mock_helper_reveal_all,stub:mock_helper_stub.A,stubMember:mock_helper_stub_member.A,throwOnConsole:mock_helper_console_throw,touch:mock_helper_touch,trigger:mock_helper_trigger},ngMocks=mock_helper_object;var mock_service=__webpack_require__(839);const defaultValue={};function MockProviders(...e){return e.map((e=>MockProvider(e,defaultValue)))}function MockProvider(e,o=defaultValue,t,r={}){func_import_exists(e,"MockProvider");const{deps:n,multi:s}="boolean"==typeof r?{deps:void 0,multi:r}:Array.isArray(r)?{deps:r,multi:void 0}:r;return t?{provide:e,[t]:o,deps:n,multi:s}:(0,helper_use_factory.A)(e,(()=>(0,mock_service.K)(e)),(e=>o===defaultValue?e:e?(0,mock_helper_stub.A)(e,o):o))}const applyOverride=(e,o)=>{(0,func_is_ng_def.p)(e,"c")?testing_.TestBed.overrideComponent(e,o):(0,func_is_ng_def.p)(e,"d")?testing_.TestBed.overrideDirective(e,o):(0,func_is_ng_def.p)(e,"m")&&testing_.TestBed.overrideModule(e,o),((0,func_is_ng_def.p)(e,"t")||(0,func_is_ng_def.p)(e,"i"))&&testing_.TestBed.overrideProvider(e,o)},ng_mocks_global_overrides_applyOverrides=e=>{for(const[o,[t,r]]of(0,core_helpers.Nn)(e))testing_.TestBed.ngMocksOverrides.set(o,Object.assign(Object.assign({},r),{override:t})),applyOverride(o,t)},applyNgMocksOverrides=e=>{var o;if(null===(o=e.ngMocksOverrides)||void 0===o?void 0:o.size){ngMocks.flushTestBed();for(const[o,t]of(0,core_helpers.Nn)(e.ngMocksOverrides))applyOverride(o,t)}e.ngMocksOverrides=void 0},initTestBed=()=>{testing_.TestBed.ngMocksSelectors||(0,core_define_property.A)(testing_.TestBed,"ngMocksSelectors",new Map),testing_.TestBed.ngMocksOverrides||(0,core_define_property.A)(testing_.TestBed,"ngMocksOverrides",new Map)},generateTouches=(e,o)=>{for(const t of core_config.A.dependencies)for(const r of e[t]?(0,core_helpers.Bq)(e[t]):[]){const e=(0,func_get_type.A)(r);if((0,func_is_ng_module_def_with_providers.h)(r)&&generateTouches(r,o),!o.has(e)&&(o.add(e),"function"==typeof e)){if(!Object.prototype.hasOwnProperty.call(e,"__ngMocksTouches")){const o=new Set,t=core_reflect_meta(e);(0,core_define_property.A)(e,"__ngMocksTouches",o,!1),t&&generateTouches(t,o)}(0,core_helpers.LG)(e.__ngMocksTouches,o)}}},defineTouches=(e,o,t)=>{var r;let n=t;return!n&&ng_mocks_universe.A.getDefaults().size>0&&(n=func_extract_tokens(e._providers||(null===(r=e._compiler)||void 0===r?void 0:r.providers)).touches,n||(n=new Set,o.providers=o.providers||[],o.providers.push({provide:core_tokens.Em,useValue:n})),generateTouches(o,n)),n},applyPlatformOverrideDef=e=>{const o=(0,func_get_type.A)(e);if(testing_.TestBed.ngMocksOverrides.has(o))return;const t=core_reflect_module_resolve(o),r=get_override_def(t);r&&(testing_.TestBed.ngMocksOverrides.set(o,{set:t}),testing_.TestBed.overrideModule(o,{set:r}))},applyPlatformOverridesBasedOnProvidedIn=(e,o)=>{const t=core_reflect_provided_in(e);t&&("string"==typeof t||o.has(t))&&(testing_.TestBed.ngMocksOverrides.set(e,{}),testing_.TestBed.overrideProvider(e,MockProvider(e)))},applyPlatformOverridesBasedOnDefaults=e=>{for(const[o,[t]]of(0,core_helpers.Nn)(ng_mocks_universe.A.getDefaults()))"mock"===t&&((0,func_is_ng_def.p)(o,"i")||(0,func_is_ng_def.p)(o,"t"))&&(e.has(o)||testing_.TestBed.ngMocksOverrides.has(o)||applyPlatformOverridesBasedOnProvidedIn(o,e))},applyPlatformOverrides=(e,o)=>{if(testing_.TestBed.ngMocksOverrides){const t=ng_mocks_universe.A.touches;ng_mocks_universe.A.touches=o;for(const o of(0,core_helpers.Bq)(e.ngModule||[]))applyPlatformOverrideDef(o);applyPlatformOverridesBasedOnDefaults(o),ng_mocks_universe.A.touches=t}},ng_mocks_global_overrides_configureTestingModule=(e,o)=>t=>{var r,n;initTestBed();const s=!("object"!=typeof t||!t||t.providers&&-1!==t.providers.indexOf(MockBuilder));let c=0;const _=[];for(const e of s?["imports","declarations"]:[])for(const o of(0,core_helpers.Bq)(t[e]))o&&(_.push([(0,func_is_ng_module_def_with_providers.h)(o)?{ngModule:getSourceOfMock(o.ngModule),providers:o.providers}:getSourceOfMock(o),(0,func_is_ng_module_def_with_providers.h)(o)?o.ngModule:o,isMockNgDef((0,func_get_type.A)(o))]),c|=_[_.length-1][2]?2:1);let i=3===c?void 0:t;if(!i){let e=MockBuilder(core_tokens.gG);for(const[o,t,n]of _){const s=null===(r=t.prototype.__ngMocksConfig)||void 0===r?void 0:r.transform,c={export:!(0,func_is_ng_def.p)(o,"m"),exportAll:!1,onRoot:!0};e=n&&s?e.mock(o,s,c):n?e.mock(o,c):e.keep(o,c)}i=e.build(),i=Object.assign(Object.assign(Object.assign({},t),i),{providers:[...null!==(n=t.providers)&&void 0!==n?n:[],...i.providers]})}const a=(0,testing_.getTestBed)(),l=func_extract_tokens(i.providers),{mocks:p,overrides:u}=l,d=defineTouches(a,i,l.touches);return p&&ngMocks.flushTestBed(),u&&ng_mocks_global_overrides_applyOverrides(u),!d||a._instantiated||a._testModuleRef||applyPlatformOverrides(a,d),e.call(o,i)},ng_mocks_global_overrides_resetTestingModule=(e,o)=>()=>(ng_mocks_universe.A.global.delete("builder:config"),ng_mocks_universe.A.global.delete("builder:module"),testing_.TestBed.ngMocksSelectors=void 0,applyNgMocksOverrides(testing_.TestBed),e.call(o)),patchVcrInstance=e=>{if(!core_.ViewContainerRef.ngMocksOverridesPatched&&((0,core_define_property.A)(core_.ViewContainerRef,"ngMocksOverridesPatched",!0),e.createComponent)){const o=e.createComponent,t=(0,helper_create_clone.A)(o,void 0,void 0,(function(e,...t){var r;const n=core_injector(core_tokens.en,this.injector);return o.apply(this,[null!==(r=null==n?void 0:n.get(e))&&void 0!==r?r:e,...t])}));(0,core_define_property.A)(e.constructor.prototype,"createComponent",t,!0),(0,core_define_property.A)(e,"createComponent",t,!0)}},createComponent=(e,o)=>t=>{const r=e.call(o,t);try{const e=r.debugElement.injector.get(core_.ViewContainerRef);patchVcrInstance(e)}catch(e){}return r},viewContainerInstall=()=>{const e=core_.ViewContainerRef;if(!e.ngMocksOverridesInstalled){const o=e.__NG_ELEMENT_ID__;o&&(0,core_define_property.A)(e,"__NG_ELEMENT_ID__",(0,helper_create_clone.A)(o,void 0,void 0,((...e)=>{const t=o.apply(o,e);return patchVcrInstance(t),t})),!0),(0,core_define_property.A)(testing_.TestBed,"createComponent",createComponent(testing_.TestBed.createComponent,testing_.TestBed)),(0,core_define_property.A)(core_.ViewContainerRef,"ngMocksOverridesInstalled",!0)}},installInjector=e=>{if(e.constructor.prototype.__ngMocksInjector||!e.constructor.prototype.get)return e;(0,core_define_property.A)(e.constructor.prototype,"__ngMocksInjector",!0);const o=e.constructor.prototype.get;return e.constructor.prototype.get=(0,helper_create_clone.A)(o,void 0,void 0,(function(e,...t){const r=o.call(this,e,...t);return r&&"object"==typeof r&&"function"==typeof r.constructor&&"string"==typeof r.constructor.name&&"Injector"===r.constructor.name.slice(-8)&&installInjector(r),r})),e},install=()=>{if(!testing_.TestBed.ngMocksOverridesInstalled){const e=mock_helper_faster_install();viewContainerInstall(),-1===e.before.indexOf(ng_mocks_global_overrides_configureTestingModule)&&e.before.push(ng_mocks_global_overrides_configureTestingModule),-1===e.after.indexOf(ng_mocks_global_overrides_resetTestingModule)&&e.after.push(ng_mocks_global_overrides_resetTestingModule),(0,core_define_property.A)(testing_.TestBed,"ngMocksOverridesInstalled",!0);const o=core_.Injector.create;core_.Injector.create=(0,helper_create_clone.A)(o,void 0,void 0,((...e)=>installInjector(o.apply(core_.Injector,e))));try{core_.Injector.create({length:0,providers:[]})}catch(e){}}};function isMockedNgDefOf(e,o,t){return"function"==typeof e&&e.mockOf===o&&(!t||(0,func_is_ng_def.p)(e,t))}install();const getMock=(e,o,t)=>{if(t&&!t.has(o))throw new Error(`There is no mock for ${(0,func_get_name.A)(o)}`);let r=t?t.get(o):void 0;return r===o&&(r=void 0),r||o===e?!r&&ng_mocks_universe.A.cacheDeclarations.has(o)&&(r=ng_mocks_universe.A.cacheDeclarations.get(o)):r=e,r};function getMockedNgDefOf(e,o){var t;const r=null!==(t=e.mockOf)&&void 0!==t?t:e,n=core_injector(core_tokens.en),s=getMock(e,r,n);if(s&&!o)return s;if(s&&o&&isMockedNgDefOf(s,r,o))return s;throw new Error(`There is no mock for ${(0,func_get_name.A)(r)}`)}function isMockOf(e,o,t){return func_is_mock(e)&&e.constructor===o&&(t?(0,func_is_ng_def.p)(e.constructor,t):(0,func_is_ng_def.p)(e.constructor))}const isMockValidator=e=>!!func_is_mock(e)&&!!e.__ngMocksConfig.isValidator,mock_instance_forgot_reset=e=>{const o=[];for(;e.length>0;){const[t,r]=e.pop()||[];r===ng_mocks_universe.A.configInstance.get(t)&&o.push("function"==typeof t?(0,func_get_name.A)(t):t)}if(o.length>0){const e=ng_mocks_universe.A.global.get("flags"),t=[`MockInstance: side effects have been detected (${o.join(", ")}).`,"Forgot to add MockInstance.scope() or to call MockInstance.restore()?"].join(" ");if("warn"===e.onMockInstanceRestoreNeed)console.warn(t);else if("throw"===e.onMockInstanceRestoreNeed)throw new Error(t)}};let currentStack;ng_mocks_stack.subscribePush((e=>{currentStack=e})),ng_mocks_stack.subscribePop(((e,o)=>{for(const o of e.mockInstance||[])if(ng_mocks_universe.A.configInstance.has(o)){const e=ng_mocks_universe.A.configInstance.get(o);e.overloads.pop(),ng_mocks_universe.A.configInstance.set(o,Object.assign({},e))}currentStack=o[o.length-1]}));const parseMockInstanceArgs=e=>{const o={};return"string"==typeof e[0]?(o.key=e[0],o.value=e[1],o.accessor=e[2]):(o.value=e[0],o.value&&"object"==typeof o.value&&(o.value=o.value.init)),o},checkReset=[];let checkCollect=!1;"undefined"!=typeof beforeEach&&(beforeEach((()=>checkCollect=!0)),beforeEach((()=>mock_instance_forgot_reset(checkReset))),afterEach((()=>checkCollect=!1)));const mockInstanceConfig=(e,o,t,r)=>{var n;const s=ng_mocks_universe.A.configInstance.has(e)?ng_mocks_universe.A.configInstance.get(e):{},c=s.overloads||[];c.push([o,t,r]),s.overloads=c,ng_mocks_universe.A.configInstance.set(e,Object.assign({},s));const _=null!==(n=currentStack.mockInstance)&&void 0!==n?n:[];return _.push(e),currentStack.mockInstance=_,checkCollect&&checkReset.push([e,ng_mocks_universe.A.configInstance.get(e),currentStack]),t};function MockInstance(e,...o){if(func_import_exists(e,"MockInstance"),o.length>0){const{key:t,value:r,accessor:n}=parseMockInstanceArgs(o);return mockInstanceConfig(e,t,r,n)}const t=ng_mocks_universe.A.configInstance.get(e)||{};ng_mocks_universe.A.configInstance.set(e,Object.assign(Object.assign({},t),{overloads:[]}));for(let o=checkReset.length-1;o>=0;o-=1)checkReset[o][0]===e&&checkReset[o][2]===currentStack&&checkReset.splice(o,1)}function MockReset(){ng_mocks_universe.A.configInstance.clear()}function MockDeclarations(...e){return e.map(MockDeclaration)}function MockDeclaration(e){if((0,func_is_ng_def.p)(e,"p"))return MockPipe(e);if((0,func_is_ng_def.p)(e,"c"))return MockComponent(e);if((0,func_is_ng_def.p)(e,"d"))return MockDirective(e);throw error_jest_mock(e),new Error(["MockDeclaration does not know how to mock","function"==typeof e?(0,func_get_name.A)(e):e].join(" "))}!function(e){e.remember=function(){ng_mocks_stack.stackPush()},e.restore=function(){ng_mocks_stack.stackPop()},e.scope=function(o="case"){"all"!==o&&"suite"!==o||(beforeAll(e.remember),afterAll(e.restore)),"all"!==o&&"case"!==o||(beforeEach(e.remember),afterEach(e.restore))}}(MockInstance||(MockInstance={}));const generateTemplateAttrWrap=(e,o)=>"i"===o?`[${e}]`:`(${e})`,generateTemplateAttrWithParams=(e,o)=>{let t=` ${generateTemplateAttrWrap(e,o)}="`;return t+="i"===o?e:`__ngMocksOutput('${e}', $event)`,t+='"',t},generateTemplateAttr=(e,o,t)=>{if(!e&&"o"===t)return"";let r="";const n=null!=e?e:o;for(const e of o){const{name:o,alias:s}=(0,func_directive_io_parse.A)(e);r+=-1===n.indexOf(s||o)?"":generateTemplateAttrWithParams(s||o,t)}return r},func_generate_template=(e,{selector:o,bindings:t,inputs:r,outputs:n})=>{let s="";return"string"==typeof e?s=e:(0,func_is_ng_def.p)(e,"p")&&t&&-1!==t.indexOf("$implicit")?s=`{{ $implicit | ${core_reflect_pipe_resolve(e).name} }}`:o&&(s+=`<${o}`,s+=generateTemplateAttr(t,r,"i"),s+=generateTemplateAttr(t,n,"o"),s+=`>`),s},generateWrapperOutput=e=>(o,t)=>"function"==typeof e[o]?e[o](t):e[o]&&"object"==typeof e[o]&&"function"==typeof e[o].emit?e[o].emit(t):e[o]&&"object"==typeof e[o]&&"function"==typeof e[o].next?e[o].next(t):void(e[o]=t),generateWrapperComponent=({bindings:e,options:o,inputs:t})=>{class r{constructor(){(0,core_define_property.A)(this,"__ngMocksOutput",generateWrapperOutput(this));let o=0;if((0,helper_define_property_descriptor.A)(this,"__ngContext__",{get:()=>o,set:e=>o=e,enumerable:!1}),!e)for(const e of t||[]){let o=null;(0,helper_define_property_descriptor.A)(this,e,{get:()=>o,set:e=>o=e})}}}return(0,core_define_property.A)(r.prototype,`__ngMocks_index_${ng_mocks_universe.A.index()}`,void 0,!1),(0,core_.Component)(o)(r),r},generateWrapperDirective=({selector:e,options:o})=>{class t{}return(0,core_.Directive)({selector:e,providers:o.providers})(t),t},getCache=()=>{var e;const o=null!==(e=ng_mocks_universe.A.config.get("MockRenderCaches"))&&void 0!==e?e:[];return 0===o.length&&ng_mocks_universe.A.config.set("MockRenderCaches",o),o},checkCache=(e,o)=>{for(const t of e){if(t.cacheKey.length!==o.length)continue;let e=!0;for(let r=0;r{var n,s,c;const _=getCache(),i=[e,...null!=t?t:[null],...null!==(n=r.providers)&&void 0!==n?n:[null],...null!==(s=r.viewProviders)&&void 0!==s?s:[null]];let a=checkCache(_,i);if(a)return a;const l=o.inputs?[...o.inputs]:[],p=o.outputs?[...o.outputs]:[];if(o.hostDirectives)for(const e of o.hostDirectives)"object"==typeof e&&e.directive&&(e.inputs&&l.push(...e.inputs),e.outputs&&p.push(...e.outputs));const u=func_generate_template(e,{selector:o.selector,inputs:l,outputs:p,bindings:t}),d={providers:r.providers,selector:"mock-render",template:u,viewProviders:r.viewProviders};if(a=generateWrapperComponent(Object.assign(Object.assign({},o),{bindings:t,options:d})),(0,core_define_property.A)(a,"cacheKey",i),(0,core_define_property.A)(a,"tpl",u),o.selector&&d.providers){const e=generateWrapperDirective(Object.assign(Object.assign({},o),{bindings:t,options:d}));(0,core_define_property.A)(a,"providers",e)}return _.unshift(a),_.splice(null!==(c=ng_mocks_universe.A.global.get("mockRenderCacheSize"))&&void 0!==c?c:core_config.A.mockRenderCacheSize),a},createPropertyGet=(e,o,t)=>{const r=()=>{if("function"==typeof t[e]){if(o[`__ngMocks_${e}__origin`]!==t[e]){const r=helper_mock_service.A.createClone(t[e],o,t);(0,core_define_property.A)(o,`__ngMocks_${e}`,r),(0,core_define_property.A)(o,`__ngMocks_${e}__origin`,t[e])}return o[`__ngMocks_${e}`]}return t[e]};return(0,core_define_property.A)(r,"__ngMocksProxy",!0),r},createPropertySet=(e,o,t)=>{const r=r=>{o[`__ngMocks_${e}`]&&(o[`__ngMocks_${e}`]=void 0),o[`__ngMocks_${e}__origin`]&&(o[`__ngMocks_${e}__origin`]=void 0),t[e]=r};return(0,core_define_property.A)(r,"__ngMocksProxy",!0),r},extractAllKeys=e=>[...helper_mock_service.A.extractPropertiesFromPrototype(Object.getPrototypeOf(e)),...helper_mock_service.A.extractMethodsFromPrototype(Object.getPrototypeOf(e)),...Object.keys(e)],extractOwnKeys=e=>[...Object.getOwnPropertyNames(e),...Object.keys(e)],func_install_prop_reader=(e,o,t,r=!1)=>{if(!o)return;(0,core_define_property.A)(e,"__ngMocks__source",o);const n=extractOwnKeys(e),s=[...extractAllKeys(o),...t];for(const t of s)(r||-1===n.indexOf(t))&&((0,helper_define_property_descriptor.A)(e,t,{get:createPropertyGet(t,e,o),set:createPropertySet(t,e,o)}),n.push(t))},registerTemplateMiddleware=(e,o)=>{const t=(0,core_helpers.He)(e),r={provide:e,useExisting:t};o.providers=[...o.providers||[],r];let n={};try{const t=testing_.TestBed.ngMocksOverrides,{override:s}=t.get(e);n=Object.assign({},s.set),n.providers=n.providers?[...n.providers,r]:o.providers}catch(e){}const s=!0===o.__ngMocksStandalone;((0,func_is_ng_def.p)(e,"c")?core_.Component:core_.Directive)(Object.assign(Object.assign(Object.assign({},o),n),s?{standalone:s}:{}))(t),testing_.TestBed.configureTestingModule({[s?"imports":"declarations"]:[t]})},func_reflect_template=e=>{var o;if(!(0,func_is_ng_def.p)(e,"c")&&!(0,func_is_ng_def.p)(e,"d"))return{};const t=core_reflect_directive_resolve(e),r={};for(const e of Object.keys(t))"standalone"!==e?r[e]=t[e]:(0,core_define_property.A)(r,"__ngMocksStandalone",!!t[e]);return r.selector&&/[\s,[\]]/.test(r.selector)&&(r.selector=""),r.selector||(r.selector=(null===(o=testing_.TestBed.ngMocksSelectors)||void 0===o?void 0:o.get(e))||"",r.selector||(r.selector=`ng-mocks-${e.name}`,registerTemplateMiddleware(e,r),testing_.TestBed.ngMocksSelectors&&testing_.TestBed.ngMocksSelectors.set(e,r.selector))),r},renderDeclaration=(e,o,t)=>{e.point=e.debugElement.children[0]&&"#text"!==e.debugElement.children[0].nativeElement.nodeName&&"#comment"!==e.debugElement.children[0].nativeElement.nodeName?e.debugElement.children[0]:e.debugElement,(0,func_is_ng_def.p)(o,"d")?(0,helper_define_property_descriptor.A)(e.point,"componentInstance",{get:()=>ngMocks.get(e.point,o)}):(0,func_is_ng_def.p)(o,"p")&&(0,helper_define_property_descriptor.A)(e.point,"componentInstance",{get:()=>ngMocks.findInstance(e.point,o)}),tryWhen(!t,(()=>func_install_prop_reader(e.componentInstance,e.point.componentInstance,[])))},renderInjection=(e,o,t)=>{let r;try{r=(0,core_helpers.Ah)(o)}catch(e){if((0,func_is_ng_def.p)(o,"p"))throw new Error([`Cannot render ${(0,func_get_name.A)(o)}.`,"Did you forget to set $implicit param, or add the pipe to providers?","https://ng-mocks.sudo.eu/guides/pipe"].join(" "));throw e}t&&ngMocks.stub(r,t),e.point=(0,mock_service.K)(core_.DebugElement,{childNodes:[],children:[],componentInstance:r,nativeElement:(0,mock_service.K)(HTMLElement)}),func_install_prop_reader(e.componentInstance,e.point.componentInstance,[],!0)},tryWhen=(e,o)=>{if(e)try{o()}catch(e){}},fixtureMessage=["Forgot to flush TestBed?","MockRender cannot be used without a reset after TestBed.get / TestBed.inject / TestBed.createComponent and another MockRender in the same test.","If you want to mock a service before rendering, consider usage of MockRenderFactory or MockInstance.","To flush TestBed, add a call of ngMocks.flushTestBed() before the call of MockRender, or pass `reset: true` to MockRender options."].join(" "),handleFixtureError=e=>{const o=new Error(fixtureMessage);throw(0,core_define_property.A)(o,"parent",e),o},flushTestBed=e=>{const o=ng_mocks_universe.A.global.get("flags"),t=(0,testing_.getTestBed)();e.reset||!t._instantiated&&!t._testModuleRef?ngMocks.flushTestBed():"throw"!==o.onTestBedFlushNeed&&(t._instantiated||t._testModuleRef)&&("warn"===o.onTestBedFlushNeed&&console.warn(fixtureMessage),ngMocks.flushTestBed())},generateFactoryInstall=(e,o)=>()=>{var t;const r=(0,testing_.getTestBed)(),n=(null===(t=r._compiler)||void 0===t?void 0:t.declarations)||r.declarations||r._declarations;if(!n||-1===n.indexOf(e)){flushTestBed(o);try{const o=[];e.providers&&o.push(e.providers),o.push(e),testing_.TestBed.configureTestingModule({declarations:o})}catch(e){handleFixtureError(e)}}},generateFactory=(e,o,t,r)=>{const n=(r,s)=>{n.configureTestBed();const c=testing_.TestBed.createComponent(e);return func_install_prop_reader(c.componentInstance,null!=r?r:{},null!=o?o:[]),(0,core_define_property.A)(c,"ngMocksStackId",ng_mocks_universe.A.global.get("bullet:stack:id")),(void 0===s||s)&&c.detectChanges(),"string"==typeof t||(0,func_is_ng_def.p)(t,"c")||(0,func_is_ng_def.p)(t,"d")||e.tpl&&(0,func_is_ng_def.p)(t,"p")?renderDeclaration(c,t,r):renderInjection(c,t,r),c};return n.declaration=e,n.bindings=o,n.configureTestBed=generateFactoryInstall(e,r),n};function MockRenderFactory(e,o,t={}){func_import_exists(e,"MockRender");const r="string"==typeof e||(0,func_is_ng_def.p)(e,"t")?{}:func_reflect_template(e),n=func_create_wrapper(e,r,o,t),s=generateFactory(n,o,e,t);return"root"!==ng_mocks_stack.current().level&&!1!==t.configureTestBed&&s.configureTestBed(),s}function MockRender(e,o,t=!0){const r=0===arguments.length?"":e,n=o&&"object"==typeof o?Object.keys(o):o,s="boolean"==typeof t?{detectChanges:t}:Object.assign({},t);return MockRenderFactory(r,n,s)(o,s.detectChanges)}var __webpack_exports__IMockBuilder=__webpack_exports__.rX,__webpack_exports__IMockBuilderConfig=__webpack_exports__.HA,__webpack_exports__IMockBuilderConfigAll=__webpack_exports__.Wn,__webpack_exports__IMockBuilderConfigComponent=__webpack_exports__.Me,__webpack_exports__IMockBuilderConfigDirective=__webpack_exports__.I7,__webpack_exports__IMockBuilderConfigModule=__webpack_exports__.tv,__webpack_exports__IMockBuilderExtended=__webpack_exports__.OG,__webpack_exports__IMockBuilderProvider=__webpack_exports__.sF,__webpack_exports__IMockBuilderResult=__webpack_exports__.me,__webpack_exports__LegacyControlValueAccessor=__webpack_exports__.bk,__webpack_exports__Mock=__webpack_exports__.JT,__webpack_exports__MockBuilder=__webpack_exports__._V,__webpack_exports__MockComponent=__webpack_exports__.Am,__webpack_exports__MockComponents=__webpack_exports__.$u,__webpack_exports__MockControlValueAccessor=__webpack_exports__.D2,__webpack_exports__MockDeclaration=__webpack_exports__.dw,__webpack_exports__MockDeclarations=__webpack_exports__.uV,__webpack_exports__MockDirective=__webpack_exports__.Cc,__webpack_exports__MockDirectives=__webpack_exports__.nr,__webpack_exports__MockInstance=__webpack_exports__.Wm,__webpack_exports__MockModule=__webpack_exports__.BN,__webpack_exports__MockPipe=__webpack_exports__.ZJ,__webpack_exports__MockPipes=__webpack_exports__.yI,__webpack_exports__MockProvider=__webpack_exports__.Qo,__webpack_exports__MockProviders=__webpack_exports__.dD,__webpack_exports__MockRender=__webpack_exports__.Ty,__webpack_exports__MockRenderFactory=__webpack_exports__.P0,__webpack_exports__MockReset=__webpack_exports__.mB,__webpack_exports__MockService=__webpack_exports__.KH,__webpack_exports__MockValidator=__webpack_exports__.b6,__webpack_exports__MockedComponent=__webpack_exports__.Je,__webpack_exports__MockedDirective=__webpack_exports__.T4,__webpack_exports__MockedModule=__webpack_exports__.qZ,__webpack_exports__MockedPipe=__webpack_exports__.cm,__webpack_exports__NG_MOCKS=__webpack_exports__.en,__webpack_exports__NG_MOCKS_GUARDS=__webpack_exports__.ZG,__webpack_exports__NG_MOCKS_INTERCEPTORS=__webpack_exports__.rO,__webpack_exports__NG_MOCKS_OVERRIDES=__webpack_exports__.So,__webpack_exports__NG_MOCKS_RESOLVERS=__webpack_exports__.C3,__webpack_exports__NG_MOCKS_ROOT_PROVIDERS=__webpack_exports__.gG,__webpack_exports__NG_MOCKS_TOUCHES=__webpack_exports__.Em,__webpack_exports__getInjection=__webpack_exports__.Ah,__webpack_exports__getMockedNgDefOf=__webpack_exports__.xz,__webpack_exports__getSourceOfMock=__webpack_exports__.VK,__webpack_exports__getTestBedInjection=__webpack_exports__.d5,__webpack_exports__isMockControlValueAccessor=__webpack_exports__.IA,__webpack_exports__isMockNgDef=__webpack_exports__.Bt,__webpack_exports__isMockOf=__webpack_exports__.AW,__webpack_exports__isMockValidator=__webpack_exports__.lt,__webpack_exports__isMockedNgDefOf=__webpack_exports__.Fk,__webpack_exports__isNgDef=__webpack_exports__.pA,__webpack_exports__isNgInjectionToken=__webpack_exports__.SM,__webpack_exports__ngMocks=__webpack_exports__.H5;export{__webpack_exports__IMockBuilder as IMockBuilder,__webpack_exports__IMockBuilderConfig as IMockBuilderConfig,__webpack_exports__IMockBuilderConfigAll as IMockBuilderConfigAll,__webpack_exports__IMockBuilderConfigComponent as IMockBuilderConfigComponent,__webpack_exports__IMockBuilderConfigDirective as IMockBuilderConfigDirective,__webpack_exports__IMockBuilderConfigModule as IMockBuilderConfigModule,__webpack_exports__IMockBuilderExtended as IMockBuilderExtended,__webpack_exports__IMockBuilderProvider as IMockBuilderProvider,__webpack_exports__IMockBuilderResult as IMockBuilderResult,__webpack_exports__LegacyControlValueAccessor as LegacyControlValueAccessor,__webpack_exports__Mock as Mock,__webpack_exports__MockBuilder as MockBuilder,__webpack_exports__MockComponent as MockComponent,__webpack_exports__MockComponents as MockComponents,__webpack_exports__MockControlValueAccessor as MockControlValueAccessor,__webpack_exports__MockDeclaration as MockDeclaration,__webpack_exports__MockDeclarations as MockDeclarations,__webpack_exports__MockDirective as MockDirective,__webpack_exports__MockDirectives as MockDirectives,__webpack_exports__MockInstance as MockInstance,__webpack_exports__MockModule as MockModule,__webpack_exports__MockPipe as MockPipe,__webpack_exports__MockPipes as MockPipes,__webpack_exports__MockProvider as MockProvider,__webpack_exports__MockProviders as MockProviders,__webpack_exports__MockRender as MockRender,__webpack_exports__MockRenderFactory as MockRenderFactory,__webpack_exports__MockReset as MockReset,__webpack_exports__MockService as MockService,__webpack_exports__MockValidator as MockValidator,__webpack_exports__MockedComponent as MockedComponent,__webpack_exports__MockedDirective as MockedDirective,__webpack_exports__MockedModule as MockedModule,__webpack_exports__MockedPipe as MockedPipe,__webpack_exports__NG_MOCKS as NG_MOCKS,__webpack_exports__NG_MOCKS_GUARDS as NG_MOCKS_GUARDS,__webpack_exports__NG_MOCKS_INTERCEPTORS as NG_MOCKS_INTERCEPTORS,__webpack_exports__NG_MOCKS_OVERRIDES as NG_MOCKS_OVERRIDES,__webpack_exports__NG_MOCKS_RESOLVERS as NG_MOCKS_RESOLVERS,__webpack_exports__NG_MOCKS_ROOT_PROVIDERS as NG_MOCKS_ROOT_PROVIDERS,__webpack_exports__NG_MOCKS_TOUCHES as NG_MOCKS_TOUCHES,__webpack_exports__getInjection as getInjection,__webpack_exports__getMockedNgDefOf as getMockedNgDefOf,__webpack_exports__getSourceOfMock as getSourceOfMock,__webpack_exports__getTestBedInjection as getTestBedInjection,__webpack_exports__isMockControlValueAccessor as isMockControlValueAccessor,__webpack_exports__isMockNgDef as isMockNgDef,__webpack_exports__isMockOf as isMockOf,__webpack_exports__isMockValidator as isMockValidator,__webpack_exports__isMockedNgDefOf as isMockedNgDefOf,__webpack_exports__isNgDef as isNgDef,__webpack_exports__isNgInjectionToken as isNgInjectionToken,__webpack_exports__ngMocks as ngMocks}; ++import*as __WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__ from"@angular/core";import*as __WEBPACK_EXTERNAL_MODULE__angular_core_testing_89899de6__ from"@angular/core/testing";import*as __WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__ from"@angular/forms";import*as __WEBPACK_EXTERNAL_MODULE__angular_common_d12e0fe1__ from"@angular/common";import*as __WEBPACK_EXTERNAL_MODULE__angular_platform_browser_bc6fa964__ from"@angular/platform-browser";var __webpack_modules__={295:(e,o,t)=>{t.d(o,{A:()=>r});const r={flags:["cacheModule","cacheComponent","cacheDirective","cacheProvider","correctModuleExports"],mockRenderCacheSize:25,neverMockModule:["ApplicationModule","CommonModule","BrowserModule","_ApplicationModule","_CommonModule","_BrowserModule"],neverMockProvidedFunction:["DomRendererFactory2","EventManager","Injector","RendererFactory2","Sanitizer","DomSanitizer","DomSanitizerImpl","ApplicationInitStatus","ApplicationRef","Compiler","IterableDiffers","KeyValueDiffers","_DomRendererFactory2","_EventManager","_Injector","_Sanitizer","_DomSanitizer","_DomSanitizerImpl","_ApplicationInitStatus","_ApplicationRef","_Compiler","_IterableDiffers","_KeyValueDiffers"],neverMockToken:["InjectionToken Set Injector scope.","InjectionToken EventManagerPlugins","InjectionToken HammerGestureConfig","InjectionToken AppId","InjectionToken DefaultCurrencyCode","InjectionToken LocaleId","InjectionToken SCHEDULER_TOKEN"],onMockBuilderMissingDependency:"throw",onMockInstanceRestoreNeed:"warn",onTestBedFlushNeed:"warn",dependencies:["declarations","hostDirectives","entryComponents","bootstrap","providers","viewProviders","imports","exports"]}},174:(e,o,t)=>{t.d(o,{A:()=>n});var r=t(932);const n=(e,o,t,n=!1)=>{r.A.definePropertyDescriptor(e,o,{configurable:!0,enumerable:n,value:t,writable:!0})}},456:(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.d(__webpack_exports__,{Ah:()=>getInjection,Bq:()=>flatten,He:()=>extendClass,LG:()=>mapValues,Nn:()=>mapEntries,by:()=>extractDependency,d4:()=>mapKeys,d5:()=>getTestBedInjection});var _angular_core_testing__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(957),_core_define_property__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(174),_core_reflect_parameters_resolve__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(749),_func_get_global__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(102),_func_get_name__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__(970),_ng_mocks_universe__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(73);const getTestBedInjection=e=>{try{return getInjection(e)}catch(e){return}},getInjection=e=>{const o=(0,_angular_core_testing__WEBPACK_IMPORTED_MODULE_0__.getTestBed)();return o.inject?o.inject(e):o.get(e)},flatten=(e,o=[])=>{if(Array.isArray(e))for(const t of e)flatten(t,o);else if(null!==e&&"object"==typeof e&&Array.isArray(e.ɵproviders))for(const t of e.ɵproviders)flatten(t,o);else o.push(e);return o},mapKeys=e=>{const o=[];return e.forEach(((e,t)=>o.push(t))),o},mapValues=(e,o)=>{const t=[];return o?e.forEach((e=>{o.add(e)})):e.forEach((e=>{t.push(e)})),t},mapEntries=(e,o)=>{const t=[];return o?e.forEach(((e,t)=>o.set(t,e))):e.forEach(((e,o)=>t.push([o,e]))),t},extractDependencyArray=(e,o)=>{for(const t of e){const e=t&&"object"==typeof t?t.ngMetadataName:void 0;"Optional"!==e&&"SkipSelf"!==e&&"Self"!==e&&o.add(t)}},extractDependency=(e,o)=>{if(o)for(const t of e)Array.isArray(t)?extractDependencyArray(t,o):o.add(t)},extendClassicClass=base=>{let child;const index=_ng_mocks_universe__WEBPACK_IMPORTED_MODULE_3__.A.index(),glb=(0,_func_get_global__WEBPACK_IMPORTED_MODULE_4__.A)();glb.ngMocksParent=base;try{eval(`\n var glb = typeof window === 'undefined' ? global : window;\n class MockMiddleware${index} extends glb.ngMocksParent {};\n glb.ngMocksResult = MockMiddleware${index};\n `),child=glb.ngMocksResult}catch(e){class o extends glb.ngMocksParent{}child=o}finally{glb.ngMocksResult=void 0,glb.ngMocksParent=void 0}return(0,_core_define_property__WEBPACK_IMPORTED_MODULE_1__.A)(child.prototype,`__ngMocks_index_${index}`,void 0,!1),child},extendClass=e=>{const o=extendClassicClass(e);(0,_core_define_property__WEBPACK_IMPORTED_MODULE_1__.A)(o,"name",`MockMiddleware${(0,_func_get_name__WEBPACK_IMPORTED_MODULE_5__.A)(e)}`,!0);const t=(0,_core_reflect_parameters_resolve__WEBPACK_IMPORTED_MODULE_2__.A)(e);return t.length>0&&(0,_core_define_property__WEBPACK_IMPORTED_MODULE_1__.A)(o,"parameters",[...t]),o}},749:(e,o,t)=>{t.d(o,{A:()=>n});var r=t(673);const n=e=>{var o;return null!==(o=(0,r.A)(e).parameters)&&void 0!==o?o:[]}},763:(e,o,t)=>{t.d(o,{C3:()=>i,Em:()=>s,So:()=>c,ZG:()=>_,en:()=>n,gG:()=>l,rO:()=>a});var r=t(614);const n=new r.InjectionToken("NG_MOCKS");n.__ngMocksSkip=!0;const s=new r.InjectionToken("NG_MOCKS_TOUCHES");s.__ngMocksSkip=!0;const c=new r.InjectionToken("NG_MOCKS_OVERRIDES");c.__ngMocksSkip=!0;const _=new r.InjectionToken("NG_MOCKS_GUARDS");_.__ngMocksSkip=!0;const i=new r.InjectionToken("NG_MOCKS_RESOLVERS");i.__ngMocksSkip=!0;const a=new r.InjectionToken("NG_MOCKS_INTERCEPTORS");a.__ngMocksSkip=!0;const l=new r.InjectionToken("NG_MOCKS_ROOT_PROVIDERS");l.__ngMocksSkip=!0},439:(e,o,t)=>{function r({name:e,alias:o,required:t},r=!1){return t?{name:e,alias:o,required:t}:o&&e!==o?r?o:`${e}:${o}`:r?"":e}t.d(o,{A:()=>r})},184:(e,o,t)=>{function r(e){if("string"==typeof e){const[o,t]=e.split(":").map((e=>e.trim()));return o!==t&&t?{name:o,alias:t}:{name:o}}return e}t.d(o,{A:()=>r})},285:(e,o,t)=>{t.d(o,{A:()=>r});const r=e=>"function"==typeof e&&e.__forward_ref__?e():e},102:(e,o,t)=>{t.d(o,{A:()=>r});const r=()=>"undefined"==typeof window?t.g:window},970:(e,o,t)=>{t.d(o,{A:()=>n});const r=new RegExp("[^0-9a-z]+","mgi"),n=e=>{let o;return"function"==typeof e&&e.name?o=e.name:"function"==typeof e?o="arrowFunction":"object"==typeof e&&e&&"InjectionToken"===e.ngMetadataName?o=e._desc:"object"==typeof e&&e&&"function"==typeof e.constructor&&(o=e.constructor.name),o||(o="unknown"),o.replace(r,"_")}},297:(e,o,t)=>{t.d(o,{A:()=>n});var r=t(659);const n=e=>e&&"object"==typeof e&&e.provide?e.provide:(0,r.h)(e)?e.ngModule:e&&"object"==typeof e&&e.directive?e.directive:e},218:(e,o,t)=>{t.d(o,{p:()=>p});var r=t(152),n=t(673);const s=(e,o)=>{const{decorators:t}=(0,n.A)(e);if(0===t.length)return!1;let r=1;if("Injectable"===o&&-1!==t.indexOf("Injectable"))return!0;for(;"Injectable"===t[t.length-r];)r+=1;return t[t.length-r]===o},c=(e,o)=>(!o||"m"===o)&&s(e,"NgModule"),_=(e,o)=>(!o||"c"===o)&&s(e,"Component"),i=(e,o)=>(!o||"d"===o)&&s(e,"Directive"),a=(e,o)=>(!o||"p"===o)&&s(e,"Pipe"),l=(e,o)=>(!o||"i"===o)&&s(e,"Injectable");function p(e,o){if("t"===o)return(0,r.S)(e);if("function"!=typeof e)return!1;const t=c(e,o),n=_(e,o),s=i(e,o),p=a(e,o),u=l(e,o);return t||n||s||p||u}},152:(e,o,t)=>{t.d(o,{S:()=>r});const r=e=>e&&"object"==typeof e&&"InjectionToken"===e.ngMetadataName},659:(e,o,t)=>{t.d(o,{h:()=>r});const r=e=>e&&"object"==typeof e&&"function"==typeof e.ngModule},73:(e,o,t)=>{t.d(o,{A:()=>p});var r=t(295),n=t(102),s=t(970);const c=e=>()=>(_.global.has(e)||_.global.set(e,new Map),_.global.get(e));(0,n.A)().ngMocksUniverse=(0,n.A)().ngMocksUniverse||{};const _=(0,n.A)().ngMocksUniverse;_.builtDeclarations=new Map,_.builtProviders=new Map,_.cacheDeclarations=new Map,_.cacheProviders=new Map,_.config=new Map,_.configInstance=new Map,_.flags=new Set(r.A.flags),_.global=new Map,_.touches=new Set,_.global.set("flags",{onMockBuilderMissingDependency:r.A.onMockBuilderMissingDependency,onMockInstanceRestoreNeed:r.A.onMockInstanceRestoreNeed,onTestBedFlushNeed:r.A.onTestBedFlushNeed}),_.getOverrides=c("overrides"),_.getDefaults=c("defaults"),_.getConfigMock=c("configMock");const i=e=>{{const o=_.getDefaults().get(e);if(o)return o}{const o="function"==typeof e?_.getDefaults().get(`@${(0,s.A)(e)}`):void 0;if(o)return o}return[]};_.getResolution=e=>{const o=_.config.get("ngMocksDepsResolution");if(null==o?void 0:o.has(e))return o.get(e);const[t]=i(e);return t},_.getBuildDeclaration=e=>{if(_.builtDeclarations.has(e))return _.builtDeclarations.get(e);const[o,t]=i(e);return"exclude"===o?null:o&&"keep"!==o?"replace"===o?t:void 0:e},_.hasBuildDeclaration=e=>{if(_.builtDeclarations.has(e))return void 0!==_.builtDeclarations.get(e);const[o]=i(e);return!!o&&"mock"!==o};const a=e=>_.hasBuildDeclaration(e),l=e=>_.getBuildDeclaration(e);_.isExcludedDef=e=>{const o=_.getResolution(e);return(!o||"exclude"===o)&&a(e)&&null===l(e)},_.isProvidedDef=e=>a(e)&&null!==l(e),_.getDefaults().set("@StoreDevtoolsModule",["exclude"]),_.indexValue=0,_.index=()=>_.indexValue++;const p=_},589:(e,o,t)=>{t.d(o,{A:()=>n});var r=t(20);const n=(e,o,t,n)=>{var s;const c=null!==(s=(0,r.A)(e,o))&&void 0!==s?s:{};if(!n&&c.set&&c.set.__ngMocksProxy)return c.set(t),t;const _={configurable:!0,enumerable:!0};return"get"===n&&c.set?_.set=c.set:"set"===n&&c.get&&(_.get=c.get),n?_[n]=t:(_.writable=!0,_.value=t),Object.defineProperty(e,o,_),t}},195:(e,o,t)=>{t.d(o,{A:()=>_});var r=t(331),n=t(794),s=t(20),c=t(932);const _=(e,o,t)=>{if("string"==typeof o)return c.A.mock(e,o,t);let _=e,i=o;const a=["__zone_symbol__unconfigurables"];"function"==typeof o&&(_=c.A.createClone(o),i=e,a.push(...Object.getOwnPropertyNames(_)));const l=[...(0,r.A)(i),...(0,n.A)(i)];for(const e of l){const o=-1===a.indexOf(e)?(0,s.A)(i,e):void 0;o&&Object.prototype.hasOwnProperty.call(o,"value")&&void 0===o.value||c.A.definePropertyDescriptor(_,e,o)}return _}},760:(e,o,t)=>{t.d(o,{A:()=>s});var r=t(73),n=t(589);const s=e=>{const o=[],t=r.A.configInstance.get(e);if(null==t?void 0:t.overloads)for(const[e,r,s]of t.overloads)e?o.push((o=>{(0,n.A)(o,e,r,s)})):o.push(r);return o}},365:(e,o,t)=>{t.d(o,{A:()=>_});var r=t(6),n=t(331),s=t(794),c=t(20);const _=(e,o,t,_)=>{const i=function(...r){return(_||e).apply(o===this?t:this,r)};for(const o of[...(0,n.A)(e),...(0,s.A)(e)]){const t=(0,c.A)(e,o);(0,r.A)(i,o,t)}return i}},6:(e,o,t)=>{t.d(o,{A:()=>n});var r=t(20);const n=(e,o,t)=>{if(!t||!e)return!1;if(Object.defineProperty){const n=(0,r.A)(e,o);if(!1===(null==n?void 0:n.configurable))return!1;Object.defineProperty(e,o,Object.assign(Object.assign(Object.assign({},t),{configurable:!0}),void 0===t.get&&void 0===t.set||!1===t.writable?{writable:!0}:{}))}else e[o]=t.value;return!0}},331:(e,o,t)=>{t.d(o,{A:()=>_});var r=t(970);const n=["sanitize","bypassSecurityTrustHtml","bypassSecurityTrustStyle","bypassSecurityTrustScript","bypassSecurityTrustUrl","bypassSecurityTrustResourceUrl"],s={DomSanitizer:n,Sanitizer:n},c=e=>{var o;const t=Object.getOwnPropertyNames(e);for(const n of null!==(o=s[(0,r.A)(e)])&&void 0!==o?o:[])t.push(n);return t},_=e=>{const o=[];let t=e;for(;t&&null!==Object.getPrototypeOf(t);){for(const e of c(t)){if("constructor"===e)continue;const r=Object.getOwnPropertyDescriptor(t,e);r&&(r.get||r.set)||-1!==o.indexOf(e)||o.push(e)}t=Object.getPrototypeOf(t)}return o}},794:(e,o,t)=>{t.d(o,{A:()=>r});const r=e=>{const o=[];let t=e;for(;t&&null!==Object.getPrototypeOf(t);){for(const e of Object.getOwnPropertyNames(t)){if("constructor"===e)continue;const r=Object.getOwnPropertyDescriptor(t,e);r&&(r.get||r.set)&&-1===o.indexOf(e)&&o.push(e)}t=Object.getPrototypeOf(t)}return o}},20:(e,o,t)=>{t.d(o,{A:()=>r});const r=(e,o)=>{let t=e;for(;t&&null!==Object.getPrototypeOf(t);){const e=Object.getOwnPropertyDescriptor(t,o);if(e)return e;t=Object.getPrototypeOf(t)}}},932:(e,o,t)=>{t.d(o,{A:()=>A,O:()=>M});var r=t(102),n=t(365),s=t(174),c=t(970),_=t(6),i=t(331),a=t(794),l=t(20);const p=(e,o=!1)=>{const t=p.customMockFunction&&!o?p.customMockFunction(e):e=>(n&&n(e),r);let r,n;return(0,s.A)(t,"__ngMocks",!0),(0,s.A)(t,"__ngMocksSet",(e=>n=e)),(0,s.A)(t,"__ngMocksGet",(e=>r=e)),t},u=p;var d=t(763),f=t(218),g=t(73);const k=["canActivate","canActivateChild","canDeactivate","canMatch","canLoad"],m=(e,o)=>Array.isArray(e[o])?(e=>{const o=[];for(const t of e)!g.A.isProvidedDef(t)&&g.A.isExcludedDef(d.ZG)||(o.push(t),(0,f.p)(t)||g.A.touches.add(t));return o})(e[o]):e[o],h=(e,o)=>{if(g.A.cacheDeclarations.has(e))return g.A.cacheDeclarations.get(e);if("object"!=typeof e)return e;if(o.has(e))return e;let t,r=!1;return Array.isArray(e)?[r,t]=((e,o,t)=>{const r=[];let n=!1;e.set(o,r);for(const s of o)g.A.isExcludedDef(s)?n=n||!0:(r.push(t(s,e)),n=n||r[r.length-1]!==s);return[n,r]})(o,e,h):e&&([r,t]=((e,o,t)=>{let r={},n=!1;e.set(o,r);for(const s of Object.keys(o))g.A.isExcludedDef(o[s])?n=n||!0:(r[s]=t(o[s],e),n=n||r[s]!==o[s]);for(const e of k){const o=m(r,e);o&&r[e].length!==o.length&&(n=n||!0,r=Object.assign(Object.assign({},r),{[e]:o}))}if("object"==typeof r.resolve&&r.resolve){const e={};let o=!1;for(const t of Object.keys(r.resolve)){const n=r.resolve[t];g.A.isProvidedDef(n)||!g.A.isExcludedDef(d.C3)?(e[t]=n,(0,f.p)(n)||g.A.touches.add(n)):o=o||!0}o&&(n=n||!0,r=Object.assign(Object.assign({},r),{resolve:e}))}return[n,r]})(o,e,h)),r?(Object.setPrototypeOf(t,Object.getPrototypeOf(e)),t):e};var v=t(663),b=t(465);(0,r.A)().ngMockshelperMockService=(0,r.A)().ngMockshelperMockService||{mockFunction:u,registerMockFunction:e=>{(0,r.A)().ngMockshelperMockService.mockFunction.customMockFunction=e},createClone:n.A,createMockFromPrototype:e=>{const o=(0,c.A)(e),t={};(0,s.A)(t,"__ngMocks",!0);const r=A.extractMethodsFromPrototype(e);for(const e of r)A.mock(t,e,o);const n=A.extractPropertiesFromPrototype(e);for(const e of n)A.mock(t,e,"get",o),A.mock(t,e,"set",o);return Object.setPrototypeOf(t,e),t},definePropertyDescriptor:_.A,extractMethodsFromPrototype:i.A,extractPropertiesFromPrototype:a.A,extractPropertyDescriptor:l.A,mock:(e,o,...t)=>{const{accessType:r,mockName:n}=(e=>{let o,t;return e.length>0&&"get"!==e[0]&&"set"!==e[0]?t=e[0]:e.length>0&&("get"===e[0]||"set"===e[0])&&(o=e[0],t=e[1]),{accessType:o,mockName:t}})(t),s=Object.getOwnPropertyDescriptor(e,o);if(s&&s[r||"value"])return s[r||"value"];const _=((e,o,t,r)=>`${null!=o?o:"function"==typeof t.prototype?t.prototype.name:(0,c.A)(t)}.${e}${null!=r?r:""}`)(o,n,e,r),i=A.mockFunction(_,!!r),a=((e,o,t)=>Object.assign(Object.assign(Object.assign(Object.assign({},"get"===t&&e&&e.set?{set:e.set}:{}),"set"===t&&e&&e.get?{get:e.get}:{}),t?{}:{writable:!0}),{[t||"value"]:o,configurable:!0,enumerable:!0}))(s,i,r);return a.get&&a.set&&a.get.__ngMocks&&a.set.__ngMocks&&a.set.__ngMocksSet((e=>a.get.__ngMocksGet(e))),Object.defineProperty(e,o,a),i},replaceWithMocks:e=>{const o=new Map,t=h(e,o);return o.clear(),t},resolveProvider:v.A,useFactory:b.A};const A=(0,r.A)().ngMockshelperMockService;function M(e){(0,r.A)().ngMockshelperMockService.registerMockFunction(e)}},663:(e,o,t)=>{t.d(o,{A:()=>u});var r=t(456),n=t(763),s=t(285),c=t(297),_=t(152),i=t(73),a=t(932),l=t(415);const p=(e,o,t)=>{let r=!1,n=!e;return o&&e&&!n&&(n=((e,o,...t)=>{for(const r of t)if(e[r]!==o[r])return!0;return!1})(o,e,"provide","useValue","useClass","useExisting","useFactory","deps")),(o===t&&e!==o||o!==t&&n)&&(r=!0),!r},u=(e,o,t)=>{const{provide:u,multi:d,change:f}=((e,o)=>{const t=(0,c.A)(e);return{change:()=>{o&&o()},multi:e!==t&&!!e.multi,provide:t}})(e,t);if(((e,o)=>null===i.A.builtProviders.get(o)||(o!==e&&e.deps&&(0,r.by)(e.deps,i.A.config.get("ngMocksDeps")),((e,o)=>{if(((e,o)=>i.A.builtProviders.has(n.rO)&&null===i.A.builtProviders.get(n.rO)&&(0,_.S)(o)&&"InjectionToken HTTP_INTERCEPTORS"===o.toString()&&o!==e)(e,o)){if(e.useFactory||e.useValue)return!0;const o=(0,s.A)(e.useExisting)||e.useClass;if(!i.A.builtProviders.has(o)||null===i.A.builtProviders.get(o))return!0}return!1})(e,o)))(e,u))return f();if(((e,o)=>{var t;return!(!e||"object"!=typeof e||!e.useExisting||!e.useExisting.mockOf&&(i.A.getResolution(o)&&!(null===(t=i.A.config.get(o))||void 0===t?void 0:t.__internal)||"keep"!==i.A.getResolution((0,s.A)(e.useExisting))))})(e,u))return i.A.touches.add(u),e;if(o.has(u))return((e,o)=>{let t=o;const r=i.A.builtProviders.get(e);return r&&(t=r),"function"==typeof t&&(t={provide:e,useClass:t}),t})(u,o.get(u));const g=((e,o,t)=>{var r;let n=((e,o)=>{if(i.A.builtProviders.has(o)){const t=i.A.builtProviders.get(o);return t===o?e:t}})(e,o);return!n&&i.A.flags.has("skipMock")&&"mock"!==i.A.getResolution(o)&&(null===(r=i.A.config.get("ngMocksDepsSkip"))||void 0===r||r.add(o),n=e),n||(n=(0,l.A)(e)),n=((e,o,t)=>{if(o!==e&&t&&t.useValue){const e=a.A.replaceWithMocks(t.useValue);return e===t.useValue?t:Object.assign(Object.assign({},t),{useValue:e})}return t})(e,o,n),p(n,e,o)||t(),n&&i.A.touches.add(o),n})(e,u,f);return d&&"object"==typeof g?Object.assign(Object.assign({},g),{multi:d}):g}},465:(e,o,t)=>{t.d(o,{A:()=>p});var r=t(614),n=t(456),s=t(152),c=t(73),_=t(195),i=t(760),a=t(839);const l=e=>(0,s.S)(e)||"string"==typeof e,p=(e,o,t)=>({deps:[r.Injector],provide:e,useFactory:r=>{const s=o?o():(0,a.K)(e),p=c.A.getOverrides().get(e),u=p?(0,n.LG)(p):[];return t&&u.push(t),u.push(...(0,i.A)(e)),((e,o,t,r,n)=>{let s=o;for(const o of t){const t=o(s,r);l(e)?s=t:o!==n?t&&(s=(0,_.A)(s,t)):s=t}return s})(e,s,u,r,t)}})},415:(e,o,t)=>{t.d(o,{A:()=>g});var r=t(295),n=t(297),s=t(152),c=t(73),_=t(6),i=t(20),a=t(465),l=t(839);const{neverMockProvidedFunction:p,neverMockToken:u}=r.A,d=[[e=>"boolean"==typeof e,!1],[e=>"number"==typeof e,0],[e=>"string"==typeof e,""],[e=>null===e,null]],f=(e,o,t)=>{var r;if(o===e)return t?(0,a.A)(e,(()=>{})):void 0;if(e.multi)return void(null===(r=c.A.config.get("ngMocksMulti"))||void 0===r||r.add(o));let n;return-1!==Object.keys(e).indexOf("useValue")?n=((e,o)=>(0,a.A)(o,(()=>e.useValue&&"object"==typeof e.useValue?(0,l.K)(e.useValue):(e=>{for(const[o,t]of d)if(o(e))return t})(e.useValue))))(e,o):-1!==Object.keys(e).indexOf("useExisting")?n=e:-1!==Object.keys(e).indexOf("useClass")?n=((e,o)=>c.A.builtProviders.has(e.useClass)&&c.A.builtProviders.get(e.useClass)===e.useClass?e:(0,a.A)(o,(()=>(0,l.K)(e.useClass))))(e,o):-1!==Object.keys(e).indexOf("useFactory")&&(n=(0,a.A)(o,(()=>({})))),n},g=(e,o=!1)=>{const t=(0,n.A)(e);if("mock"===c.A.getResolution(t));else{if((e=>"function"==typeof e&&-1!==p.indexOf(e.name))(t))return e;if((e=>(0,s.S)(e)&&-1!==u.indexOf(e.toString()))(t))return}const r=c.A.flags.has("cacheProvider")?c.A.cacheProviders:void 0;return t===e&&r&&r.has(t)?r.get(t):((e,o,t)=>{let r;return"function"==typeof o&&(r=((e,o)=>(0,a.A)(o,(()=>{const t=(0,l.K)(o);return o!==e&&-1!==Object.keys(e).indexOf("useClass")&&((e,o)=>{const t=Object.getOwnPropertyNames(e),r=(0,l.K)(o);for(const o of Object.getOwnPropertyNames(r)){if(-1!==t.indexOf(o))continue;const n=(0,i.A)(r,o);(0,_.A)(e,o,n)}})(t,e.useClass),t})))(e,o)),o===e&&r&&t&&t.set(o,r),r})(e,t,r)||f(e,t,o)}},839:(e,o,t)=>{t.d(o,{K:()=>a});var r=t(970),n=t(195);const s=e=>{if("function"!=typeof e)return!1;if(!e.prototype)return!0;if((e=>!!(e.ɵprov||e.__annotations__||e.__parameters__||e.parameters))(e))return!1;const o=e.toString();if(null!==o.match(/^class\b/))return!1;const t=o.match(/^function\s+([^\s(]+)\(/);return null===t||!((e,o,t)=>{if(null!==e.match(/^class/))return!0;if(Object.keys(t.prototype).length>0)return!0;const r=e.codePointAt(0);if(r&&r>=65&&r<=90&&null!==o.match(/\bthis\./gm))return!0;const n=new RegExp(`\\(this,\\s*${e}\\)`,"mg");return null!==o.match(n)})(t[1],o,e)};var c=t(932);const _=[[e=>"function"==typeof e&&!s(e),(e,o)=>{const t=c.A.createMockFromPrototype(o.prototype);return e.set(o,t),t}],[s,(e,o,t)=>{const n=c.A.mockFunction(`func:${t||(0,r.A)(o)}`);return e.set(o,n()),n}],[e=>Array.isArray(e),()=>[]],[e=>null!==e&&"object"==typeof e&&"InjectionToken"!==e.ngMetadataName&&"object"==typeof Object.getPrototypeOf(e),(e,o,t,r)=>{const n=c.A.createMockFromPrototype(o.constructor.prototype);e.set(o,n);for(const s of Object.keys(o)){const c=r(e,o[s],`${t||"instance"}.${s}`);void 0!==c&&(n[s]=c)}return Object.setPrototypeOf(n,Object.getPrototypeOf(o)),n}]],i=(e,o,t="",r)=>{const s=((e,o,t,r)=>{var n;for(const[s,c]of _)if(s(o))return null!==(n=e.get(o))&&void 0!==n?n:c(e,o,t,r)})(e,o,t,i);return r&&(0,n.A)(s,r),s};function a(e,...o){const t=o.length>0&&"string"==typeof o[0]?o[0]:o[1],r=o.length>0&&o[0]&&"object"==typeof o[0]?o[0]:void 0,n=new Map,s=i(n,e,t,r);return n.clear(),s}},673:(e,o,t)=>{t.d(o,{A:()=>x});var r=t(614),n=t(174),s=t(439),c=t(184);const _=(e,o)=>{const t=e.indexOf(o);-1!==t&&e.splice(t,1),"Injectable"!==o&&"Pipe"!==o&&"Directive"!==o&&"Component"!==o&&"NgModule"!==o||e.push(o)},i=e=>{const o=[];for(const t of Object.keys(e))o.push(t);return o},a=e=>(o,t,r,n)=>{var _;const{alias:i,required:a}=(0,c.A)({name:t,alias:null!==(_=r.alias)&&void 0!==_?_:r.bindingPropertyName,required:r.required}),l=(0,s.A)({name:t,alias:i,required:a});let p=!0;for(const o of n[e]){if(o===l){p=!1;break}const{name:e,alias:r,required:n}=(0,c.A)(o);if(e===t&&r===i&&n===a){p=!1;break}}p&&n[e].unshift(l)},l=a("inputs"),p=a("outputs"),u=e=>(o,t,r,n)=>{n.queries[t]||(n.queries[t]=Object.assign(Object.assign({isViewQuery:e,ngMetadataName:o,selector:r.selector},void 0===r.read?{}:{read:r.read}),void 0===r.static?{}:{static:r.static}))},d=u(!1),f=u(!0),g=e=>(o,t,r,n)=>{n.queries[t]||(n.queries[t]=Object.assign(Object.assign(Object.assign({isViewQuery:e,ngMetadataName:o,selector:r.selector},void 0===r.descendants?{}:{descendants:r.descendants}),void 0===r.emitDistinctChangesOnly?{}:{emitDistinctChangesOnly:r.emitDistinctChangesOnly}),void 0===r.read?{}:{read:r.read}))},k={ContentChild:d,ContentChildren:g(!1),HostBinding:(e,o,t,r)=>{const n=`[${t.hostPropertyName||o}]`;r.host[n]||(r.host[n]=o),r.hostBindings.push([o,t.hostPropertyName||o,...t.args?[t.args]:[]])},HostListener:(e,o,t,r)=>{const n=`(${t.eventName||o})`;r.host[n]||(r.host[n]=`${o}($event)`),r.hostListeners.push([o,t.eventName||o,...t.args?[t.args]:[]])},Input:l,Output:p,ViewChild:f,ViewChildren:g(!0)},m=e=>{const o=a(e);return(e,t,r,n)=>{var s;const{alias:c,required:_}=void 0===(null===(s=r.args)||void 0===s?void 0:s[0])?{}:"string"==typeof r.args[0]?{alias:r.args[0]}:r.args[0];o(e,t,{alias:c,required:_,bindingPropertyName:c},n)}},h=m("inputs"),v=m("outputs"),b=e=>(o,t,r,n)=>{n.queries[t]||(n.queries[t]=Object.assign({isViewQuery:e,ngMetadataName:o,selector:r.args[0]},r.args[1]))},A=b(!1),M=b(!0),y={ContentChild:A,ContentChildren:A,HostBinding:(e,o,t,r)=>{var n;const s=`[${(null===(n=t.args)||void 0===n?void 0:n[0])||o}]`;r.host[s]||(r.host[s]=o),r.hostBindings.push([o,...t.args||[]])},HostListener:(e,o,t,r)=>{var n;const s=`(${(null===(n=t.args)||void 0===n?void 0:n[0])||o})`;r.host[s]||(r.host[s]=`${o}($event)`),r.hostListeners.push([o,...t.args||[]])},Input:h,Output:v,ViewChild:M,ViewChildren:M},w=(e,o)=>{if(e){e.inputs=e.inputs||[];for(const t of o.inputs)-1===e.inputs.indexOf(t)&&e.inputs.push(t);e.outputs=e.outputs||[];for(const t of o.outputs)-1===e.outputs.indexOf(t)&&e.outputs.push(t);e.queries=Object.assign(Object.assign({},e.queries||[]),o.queries),e.hostBindings=o.hostBindings,e.hostListeners=o.hostListeners}},O=new r["ɵReflectionCapabilities"],C=e=>{if("function"!=typeof e&&"object"!=typeof e)return{};if(Object.prototype.hasOwnProperty.call(e,"__ngMocksParsed"))return e.__ngMocksDeclarations;const o=Object.getPrototypeOf(e),t=o?C(o):{},r=(e=>({host:e.host?Object.assign({},e.host):{},hostBindings:e.hostBindings?[...e.hostBindings]:[],hostListeners:e.hostListeners?[...e.hostListeners]:[],attributes:e.attributes?[...e.attributes]:[],inputs:e.inputs?[...e.inputs]:[],outputs:e.outputs?[...e.outputs]:[],propDecorators:e.propDecorators?Object.assign({},e.propDecorators):{},queries:e.queries?Object.assign({},e.queries):{},decorators:e.decorators?[...e.decorators]:[]}))(t);return(0,n.A)(e,"__ngMocksParsed",!0),((e,o)=>{if(Object.prototype.hasOwnProperty.call(e,"__parameters__")&&e.__parameters__)for(const t of e.__parameters__)for(const e of t||[])"Attribute"===e.ngMetadataName&&-1===o.attributes.indexOf(e.attributeName)&&o.attributes.push(e.attributeName)})(e,r),((e,o)=>{if(Object.prototype.hasOwnProperty.call(e,"__annotations__")&&e.__annotations__)for(const t of e.__annotations__){const e=null==t?void 0:t.ngMetadataName;e&&(o[e]=Object.assign(Object.assign({},t),{attributes:o.attributes}),_(o.decorators,e))}})(e,r),((e,o)=>{var t,r;if(Object.prototype.hasOwnProperty.call(e,"decorators")&&e.decorators)for(const n of e.decorators){const e=null===(r=null===(t=null==n?void 0:n.type)||void 0===t?void 0:t.prototype)||void 0===r?void 0:r.ngMetadataName;e&&(o[e]=n.args?Object.assign({},n.args[0]):{},_(o.decorators,e))}})(e,r),((e,o)=>{var t,r,n;if(Object.prototype.hasOwnProperty.call(e,"propDecorators")&&e.propDecorators)for(const s of i(e.propDecorators)){o.propDecorators[s]=[...o.propDecorators[s]||[],...e.propDecorators[s]];for(const c of e.propDecorators[s]){const e=null===(r=null===(t=null==c?void 0:c.type)||void 0===t?void 0:t.prototype)||void 0===r?void 0:r.ngMetadataName;e&&(null===(n=y[e])||void 0===n||n.call(y,e,s,c,o))}}})(e,r),((e,o)=>{var t;if(Object.prototype.hasOwnProperty.call(e,"__prop__metadata__")&&e.__prop__metadata__)for(const r of i(e.__prop__metadata__)){const n=e.__prop__metadata__[r];for(const e of n){const n=null==e?void 0:e.ngMetadataName;n&&(null===(t=k[n])||void 0===t||t.call(k,n,r,e,o))}}})(e,r),w(r.Directive,r),w(r.Component,r),(0,n.A)(e,"__ngMocksDeclarations",Object.assign(Object.assign(Object.assign({},t),r),{parameters:O.parameters(e)})),e.__ngMocksDeclarations},x=C},614:(e,o,t)=>{e.exports=(e=>{var o={};return t.d(o,e),o})({ChangeDetectorRef:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ChangeDetectorRef,Component:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Component,ComponentFactoryResolver:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ComponentFactoryResolver,ContentChild:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ContentChild,ContentChildren:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ContentChildren,DebugElement:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.DebugElement,Directive:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Directive,ElementRef:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ElementRef,EventEmitter:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.EventEmitter,InjectionToken:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.InjectionToken,Injector:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Injector,Input:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Input,NgModule:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.NgModule,Optional:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Optional,Output:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Output,Pipe:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Pipe,QueryList:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.QueryList,Self:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.Self,TemplateRef:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.TemplateRef,ViewChild:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ViewChild,ViewChildren:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ViewChildren,ViewContainerRef:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__.ViewContainerRef,ɵReflectionCapabilities:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_bcead0df__["ɵReflectionCapabilities"]})},957:(e,o,t)=>{e.exports=(e=>{var o={};return t.d(o,e),o})({TestBed:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_testing_89899de6__.TestBed,getTestBed:()=>__WEBPACK_EXTERNAL_MODULE__angular_core_testing_89899de6__.getTestBed})}},__webpack_module_cache__={};function __webpack_require__(e){var o=__webpack_module_cache__[e];if(void 0!==o)return o.exports;var t=__webpack_module_cache__[e]={exports:{}};return __webpack_modules__[e](t,t.exports,__webpack_require__),t.exports}__webpack_require__.d=(e,o)=>{for(var t in o)__webpack_require__.o(o,t)&&!__webpack_require__.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},__webpack_require__.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),__webpack_require__.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__={};__webpack_require__.d(__webpack_exports__,{rX:()=>types_namespaceObject.IMockBuilder,HA:()=>types_namespaceObject.IMockBuilderConfig,Wn:()=>types_namespaceObject.IMockBuilderConfigAll,Me:()=>types_namespaceObject.IMockBuilderConfigComponent,I7:()=>types_namespaceObject.IMockBuilderConfigDirective,tv:()=>types_namespaceObject.IMockBuilderConfigModule,OG:()=>types_namespaceObject.IMockBuilderExtended,sF:()=>types_namespaceObject.IMockBuilderProvider,me:()=>types_namespaceObject.IMockBuilderResult,bk:()=>LegacyControlValueAccessor,JT:()=>Mock,_V:()=>MockBuilder,Am:()=>MockComponent,$u:()=>MockComponents,D2:()=>mock_control_value_accessor_namespaceObject.MockControlValueAccessor,dw:()=>MockDeclaration,uV:()=>MockDeclarations,Cc:()=>MockDirective,nr:()=>MockDirectives,Wm:()=>MockInstance,BN:()=>MockModule,ZJ:()=>MockPipe,yI:()=>MockPipes,Qo:()=>MockProvider,dD:()=>MockProviders,Ty:()=>MockRender,P0:()=>MockRenderFactory,mB:()=>MockReset,KH:()=>mock_service.K,b6:()=>mock_control_value_accessor_namespaceObject.MockValidator,Je:()=>mock_component_types_namespaceObject.MockedComponent,T4:()=>mock_directive_types_namespaceObject.MockedDirective,qZ:()=>mock_module_types_namespaceObject.MockedModule,cm:()=>mock_pipe_types_namespaceObject.MockedPipe,en:()=>core_tokens.en,ZG:()=>core_tokens.ZG,rO:()=>core_tokens.rO,So:()=>core_tokens.So,C3:()=>core_tokens.C3,gG:()=>core_tokens.gG,Em:()=>core_tokens.Em,Ah:()=>core_helpers.Ah,xz:()=>getMockedNgDefOf,VK:()=>getSourceOfMock,d5:()=>core_helpers.d5,IA:()=>isMockControlValueAccessor,Bt:()=>isMockNgDef,AW:()=>isMockOf,lt:()=>isMockValidator,Fk:()=>isMockedNgDefOf,pA:()=>func_is_ng_def.p,SM:()=>func_is_ng_injection_token.S,H5:()=>ngMocks});var mock_control_value_accessor_namespaceObject={};__webpack_require__.r(mock_control_value_accessor_namespaceObject),__webpack_require__.d(mock_control_value_accessor_namespaceObject,{b:()=>LegacyControlValueAccessor});var types_namespaceObject={};__webpack_require__.r(types_namespaceObject);var mock_module_types_namespaceObject={};__webpack_require__.r(mock_module_types_namespaceObject);var mock_component_types_namespaceObject={};__webpack_require__.r(mock_component_types_namespaceObject);var mock_directive_types_namespaceObject={};__webpack_require__.r(mock_directive_types_namespaceObject);var mock_pipe_types_namespaceObject={};__webpack_require__.r(mock_pipe_types_namespaceObject);var ng_mocks_universe=__webpack_require__(73),_a,_b;const stackRoot={id:{},level:"root"},stack=ng_mocks_universe.A.global.get("reporter-stack")||[Object.assign({},stackRoot)];ng_mocks_universe.A.global.set("reporter-stack",stack);const current=()=>stack[stack.length-1],listenersPush=null!==(_a=ng_mocks_universe.A.global.get("reporter-stack-push"))&&void 0!==_a?_a:[];ng_mocks_universe.A.global.set("reporter-stack-push",listenersPush);const listenersPop=null!==(_b=ng_mocks_universe.A.global.get("reporter-stack-pop"))&&void 0!==_b?_b:[];ng_mocks_universe.A.global.set("reporter-stack-pop",listenersPop);const stackPush=()=>{const e={};ng_mocks_universe.A.global.set("reporter-stack-id",e);const o={id:e,level:"runtime"};stack.push(o);for(const e of listenersPush)e(o,stack)},stackPop=()=>{const e=stack.pop();if(0===stack.length&&stack.push("root"===(null==e?void 0:e.level)?e:Object.assign({},stackRoot)),e&&"root"!==e.level)for(const o of listenersPop)o(e,stack);ng_mocks_universe.A.global.set("reporter-stack-id",stack[stack.length-1].id)},subscribePush=e=>{listenersPush.indexOf(e)&&listenersPush.push(e),stack.length>0&&e(stack[stack.length-1],stack)},subscribePop=e=>{-1===listenersPop.indexOf(e)&&listenersPop.push(e)},unsubscribePush=e=>{const o=listenersPush.indexOf(e);-1!==o&&listenersPush.splice(o,1)},unsubscribePop=e=>{const o=listenersPop.indexOf(e);-1!==o&&listenersPop.splice(o,1)},ng_mocks_stack={current,stackPop,stackPush,subscribePop,subscribePush,unsubscribePop,unsubscribePush};var core_=__webpack_require__(614),testing_=__webpack_require__(957),core_helpers=__webpack_require__(456),core_tokens=__webpack_require__(763);const func_extract_tokens=e=>{let o,t,r;for(const n of(0,core_helpers.Bq)(e||[]))"object"==typeof n&&(n.provide===core_tokens.en&&(o=n.useValue),n.provide===core_tokens.So&&(t=n.useValue),n.provide===core_tokens.Em&&(r=n.useValue));return{mocks:o,overrides:t,touches:r}};var core_define_property=__webpack_require__(174),collect_declarations=__webpack_require__(673),func_is_ng_injection_token=__webpack_require__(152);const getNgType=e=>{if("string"==typeof e)return;if((0,func_is_ng_injection_token.S)(e))return"Injectable";const{decorators:o}=(0,collect_declarations.A)(e);for(let e=o.length-1;e>=0;e-=1)if("Injectable"!==o[e])return o[e];return o.length>0?"Injectable":void 0};function isStandalone(e){const o=getNgType(e);return"NgModule"!==o}var helper_extract_property_descriptor=__webpack_require__(20);const core_def_stack=class{constructor(){this.stack=[],this.push()}push(){this.stack.push(new Map)}pop(){var e;return null!==(e=this.stack.pop())&&void 0!==e?e:new Map}has(e){for(let o=this.stack.length-1;o>=0;o-=1)if(this.stack[o].has(e))return!0;return!1}get(e){for(let o=this.stack.length-1;o>=0;o-=1)if(this.stack[o].has(e))return this.stack[o].get(e)}set(e,o){for(let t=this.stack.length-1;t>=0;t-=1)this.stack[t].set(e,o);return this}merge(e){for(const[o,t]of(0,core_helpers.Nn)(e))this.set(o,t);return this}};var func_get_name=__webpack_require__(970),func_is_ng_def=__webpack_require__(218),func_is_ng_module_def_with_providers=__webpack_require__(659),core_config=__webpack_require__(295);class MockBuilderStash{constructor(){this.data={}}backup(){this.data={builtDeclarations:ng_mocks_universe.A.builtDeclarations,builtProviders:ng_mocks_universe.A.builtProviders,cacheDeclarations:ng_mocks_universe.A.cacheDeclarations,cacheProviders:ng_mocks_universe.A.cacheProviders,config:ng_mocks_universe.A.config,configInstance:ng_mocks_universe.A.configInstance,flags:ng_mocks_universe.A.flags,touches:ng_mocks_universe.A.touches},ng_mocks_universe.A.builtDeclarations=new Map,ng_mocks_universe.A.builtProviders=new Map,ng_mocks_universe.A.cacheDeclarations=new Map,ng_mocks_universe.A.cacheProviders=new Map,ng_mocks_universe.A.config=new Map,ng_mocks_universe.A.configInstance=new Map,ng_mocks_universe.A.flags=new Set(core_config.A.flags),ng_mocks_universe.A.touches=new Set}restore(){for(const e of Object.keys(this.data))ng_mocks_universe.A[e]=this.data[e]}}const core_reflect_provided_in=e=>{var o,t,r;if(e&&("object"==typeof e||"function"==typeof e))return null!==(t=null===(o=e.ɵprov)||void 0===o?void 0:o.providedIn)&&void 0!==t?t:null===(r=e.ngInjectableDef)||void 0===r?void 0:r.providedIn};var func_get_type=__webpack_require__(297),helper_resolve_provider=__webpack_require__(663);const add_requested_providers=(e,{providerDef:o,mockDef:t},r)=>{for(const t of(0,core_helpers.LG)(o))e.providers.push(t);for(const o of(0,core_helpers.Bq)(e.providers)){const e=(0,func_get_type.A)(o);ng_mocks_universe.A.touches.add(e),e!==o&&o.deps&&(0,core_helpers.by)(o.deps,ng_mocks_universe.A.config.get("ngMocksDeps"))}for(const o of(0,core_helpers.LG)(t))ng_mocks_universe.A.touches.has(o)||"root"!==core_reflect_provided_in(o)||(e.providers.push((0,helper_resolve_provider.A)(o,r)),ng_mocks_universe.A.touches.add(o))},apply_platform_modules=()=>{const e=(0,testing_.getTestBed)();if(e.ngModule)for(const o of(0,core_helpers.Bq)(e.ngModule))ng_mocks_universe.A.touches.add((0,func_get_type.A)(o))},error_empty_def=e=>{if(!e)throw new Error(["undefined / null has been passed into ng-mocks as a declaration / provider.","Please ensure that the current test file has correct imports:","imported files exist and imported declarations have been exported in the file."].join(" "))},func_is_jest_mock=e=>!(!e||"function"!=typeof e&&"object"!=typeof e||!(e._isMockFunction&&e.mockName&&e.__annotations__)),error_jest_mock=e=>{if(func_is_jest_mock(e))throw new Error([`ng-mocks got ${(0,func_get_name.A)(e)} which has been already mocked by jest.mock().`,"It is not possible to produce correct mocks for it, because jest.mock() removes Angular decorators.",`To fix this, please avoid jest.mock() on the file which exports ${(0,func_get_name.A)(e)} or add jest.dontMock() on it.`,"The same should be done for all related dependencies."].join(" "))},error_missing_decorators=e=>{throw new Error([`${(0,func_get_name.A)(e)} declaration has been passed into ng-mocks without Angular decorators.`,"Therefore, it cannot be properly handled.","Highly likely,","undefined"==typeof jest?"":"jest.mock() has been used on its file, or","ng-mocks is imported in production code, or got a class without Angular decoration.","Otherwise, please create an issue on github: https://github.com/help-me-mom/ng-mocks/issues/new?title=False%20positive%20ng-mocks%20not%20in%20JIT.","Thank you in advance for support."].join(" "))},core_reflect_body_catch=e=>o=>{error_empty_def(o);try{return e(o)}catch(e){error_jest_mock(o),error_missing_decorators(o)}},core_reflect_directive_resolve=e=>core_reflect_body_catch((e=>{const o=(0,collect_declarations.A)(e);if(o.Component)return o.Component;if(o.Directive)return o.Directive;throw new Error("Cannot resolve declarations")}))(e),core_reflect_module_resolve=e=>core_reflect_body_catch((e=>{const o=(0,collect_declarations.A)(e);if(o.NgModule)return o.NgModule;throw new Error("Cannot resolve declarations")}))(e),core_reflect_meta=e=>(0,func_is_ng_def.p)(e,"c")||(0,func_is_ng_def.p)(e,"d")?core_reflect_directive_resolve(e):(0,func_is_ng_def.p)(e,"m")?core_reflect_module_resolve(e):void 0;function getSourceOfMock(e){return"function"==typeof e&&e.mockOf?e.mockOf:e}const mark_exported=(e,o)=>{var t;const r=getSourceOfMock(e),n=null!==(t=ng_mocks_universe.A.configInstance.get(r))&&void 0!==t?t:{__set:!0};n.exported||(n.exported=new Set),o&&n.exported.add(getSourceOfMock(o)),n.__set&&(n.__set=void 0,ng_mocks_universe.A.configInstance.set(r,n))};var x=e=>{var o={};return __webpack_require__.d(o,e),o},y=e=>()=>e;const forms_namespaceObject=x({AbstractControl:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.AbstractControl,DefaultValueAccessor:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.DefaultValueAccessor,FormControl:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.FormControl,FormControlDirective:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.FormControlDirective,NG_ASYNC_VALIDATORS:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.NG_ASYNC_VALIDATORS,NG_VALIDATORS:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.NG_VALIDATORS,NG_VALUE_ACCESSOR:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.NG_VALUE_ACCESSOR,NgControl:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.NgControl,NgModel:()=>__WEBPACK_EXTERNAL_MODULE__angular_forms_df10eade__.NgModel}),AbstractControl=forms_namespaceObject.AbstractControl,DefaultValueAccessor=forms_namespaceObject.DefaultValueAccessor,FormControl=forms_namespaceObject.FormControl,FormControlDirective=forms_namespaceObject.FormControlDirective,NG_ASYNC_VALIDATORS=forms_namespaceObject.NG_ASYNC_VALIDATORS,NG_VALIDATORS=forms_namespaceObject.NG_VALIDATORS,NG_VALUE_ACCESSOR=forms_namespaceObject.NG_VALUE_ACCESSOR,NgControl=forms_namespaceObject.NgControl,NgModel=forms_namespaceObject.NgModel,core_form={AbstractControl,DefaultValueAccessor,FormControl,FormControlDirective,NG_ASYNC_VALIDATORS,NG_VALIDATORS,NG_VALUE_ACCESSOR,NgControl,NgModel},func_is_mock=e=>e&&"object"==typeof e&&!!e.__ngMocks;var mock_helper_stub=__webpack_require__(195),mock_instance_apply=__webpack_require__(760),helper_mock_service=__webpack_require__(932),func_directive_io_parse=__webpack_require__(184);const applyProxy=(e,o,t,r)=>{if(e.instance&&r&&(e.instance[r]=t),e.instance&&e.instance[o])return e.instance[o](t)};class MockControlValueAccessorProxy{constructor(e){this.target=e}registerOnChange(e){applyProxy(this,"registerOnChange",e,"__simulateChange")}registerOnTouched(e){applyProxy(this,"registerOnTouched",e,"__simulateTouch")}setDisabledState(e){applyProxy(this,"setDisabledState",e)}writeValue(e){applyProxy(this,"writeValue",e)}}class MockValidatorProxy{constructor(e){this.target=e}registerOnValidatorChange(e){applyProxy(this,"registerOnValidatorChange",e,"__simulateValidatorChange")}validate(e){return this.instance&&this.instance.validate?this.instance.validate(e):null}}class MockAsyncValidatorProxy{constructor(e){this.target=e}registerOnValidatorChange(e){applyProxy(this,"registerOnValidatorChange",e,"__simulateValidatorChange")}validate(e){if(this.instance&&this.instance.validate){const o=this.instance.validate(e);return void 0===o?Promise.resolve(null):o}return Promise.resolve(null)}}const setValueAccessor=(e,o)=>{if(o&&!o.valueAccessor&&e.__ngMocksConfig.setControlValueAccessor)try{o.valueAccessor=new MockControlValueAccessorProxy(e.__ngMocksCtor)}catch(e){}},installValueAccessor=(e,o)=>{e.valueAccessor.instance||e.valueAccessor.target!==o.__ngMocksCtor||(e.valueAccessor.instance=o,helper_mock_service.A.mock(o,"registerOnChange"),helper_mock_service.A.mock(o,"registerOnTouched"),helper_mock_service.A.mock(o,"setDisabledState"),helper_mock_service.A.mock(o,"writeValue"),o.__ngMocksConfig.isControlValueAccessor=!0)},installValidator=(e,o)=>{for(const t of e)t.instance||t.target!==o.__ngMocksCtor||(t.instance=o,helper_mock_service.A.mock(o,"registerOnValidatorChange"),helper_mock_service.A.mock(o,"validate"),o.__ngMocksConfig.isValidator=!0)},applyNgValueAccessor=(e,o)=>{setValueAccessor(e,o);try{o&&(installValueAccessor(o,e),installValidator(o._rawValidators,e),installValidator(o._rawAsyncValidators,e))}catch(e){}},applyOutputs=e=>{const o=[];for(const t of e.__ngMocksConfig.outputs||[])o.push((0,func_directive_io_parse.A)(t).name);for(const t of o)e[t]||Object.getOwnPropertyDescriptor(e,t)||(e[t]=new core_.EventEmitter)},applyPrototype=(e,o)=>{for(const t of[...helper_mock_service.A.extractMethodsFromPrototype(o),...helper_mock_service.A.extractPropertiesFromPrototype(o)]){const r=helper_mock_service.A.extractPropertyDescriptor(o,t);helper_mock_service.A.definePropertyDescriptor(e,t,r)}},applyMethods=(e,o)=>{for(const t of helper_mock_service.A.extractMethodsFromPrototype(o))e[t]||Object.getOwnPropertyDescriptor(e,t)||helper_mock_service.A.mock(e,t)},applyProps=(e,o)=>{for(const t of helper_mock_service.A.extractPropertiesFromPrototype(o))e[t]||Object.getOwnPropertyDescriptor(e,t)||(helper_mock_service.A.mock(e,t,"get"),helper_mock_service.A.mock(e,t,"set"))},applyOverrides=(e,o,t)=>{const r=ng_mocks_universe.A.getOverrides().get(o),n=r?(0,core_helpers.LG)(r):[];e.__ngMocksConfig.init&&n.push(e.__ngMocksConfig.init),n.push(...(0,mock_instance_apply.A)(o));for(const o of n){const r=o(e,t);r&&(0,mock_helper_stub.A)(e,r)}};class Mock{constructor(e=null,o=null){const t=this.constructor.mockOf;(0,core_define_property.A)(this,"__ngMocks",!0),(0,core_define_property.A)(this,"__ngMocksInjector",e),(0,core_define_property.A)(this,"__ngMocksCtor",this.constructor);for(const e of this.__ngMocksConfig.queryScanKeys||[])(0,core_define_property.A)(this,`__ngMocksVcr_${e}`,void 0);for(const e of this.__ngMocksConfig.hostBindings||[])helper_mock_service.A.mock(this,e,"get"),helper_mock_service.A.mock(this,e,"set");for(const e of this.__ngMocksConfig.hostListeners||[])helper_mock_service.A.mock(this,e);func_is_mock(this)&&(applyNgValueAccessor(this,o),applyOutputs(this),applyPrototype(this,Object.getPrototypeOf(this)),applyMethods(this,t.prototype),applyProps(this,t.prototype)),Object.setPrototypeOf(this,t.prototype),applyOverrides(this,t,null!=e?e:void 0)}}(0,core_define_property.A)(Mock,"parameters",[[core_.Injector,new core_.Optional],[core_form.NgControl||(()=>{}),new core_.Optional,new core_.Self]]);class LegacyControlValueAccessor extends Mock{__simulateChange(){}__simulateTouch(){}__simulateValidatorChange(){}}var func_directive_io_build=__webpack_require__(439);const decorate_inputs=(e,o,t)=>{if(o)for(const r of o){const{name:o,alias:n,required:s}=(0,func_directive_io_parse.A)(r);t&&-1!==t.indexOf(o)||(0,core_.Input)((0,func_directive_io_build.A)({name:o,alias:n,required:s},!0))(e.prototype,o)}},decorate_mock=(e,o,t={})=>{(0,core_define_property.A)(e,"mockOf",o),(0,core_define_property.A)(e,"nameConstructor",(0,func_get_name.A)(e)),(0,core_define_property.A)(e,"name",`MockOf${(0,func_get_name.A)(o)}`,!0);const r=ng_mocks_universe.A.getConfigMock().has(o)?Object.assign(Object.assign({},t),{config:Object.assign(Object.assign({},ng_mocks_universe.A.getConfigMock().get(o)),t.config)}):t;(0,core_define_property.A)(e.prototype,"__ngMocksConfig",r)},decorate_outputs=(e,o)=>{if(o)for(const t of o){const{name:o,alias:r,required:n}=(0,func_directive_io_parse.A)(t);(0,core_.Output)((0,func_directive_io_build.A)({name:o,alias:r,required:n},!0))(e.prototype,o)}},map={ContentChild:core_.ContentChild,ContentChildren:core_.ContentChildren,ViewChild:core_.ViewChild,ViewChildren:core_.ViewChildren},isInternalKey=e=>0===e.indexOf("__mock"),cloneVcrQuery=e=>Object.assign(Object.assign({},e),{ngMetadataName:e.ngMetadataName,read:core_.ViewContainerRef}),generateFinalQueries=e=>{const o=[],t=[];for(const r of Object.keys(e)){const n=e[r];o.push([r,n]),n.isViewQuery||isInternalKey(r)||(t.push(r),o.push([`__ngMocksVcr_${r}`,cloneVcrQuery(n)]))}return[o,t]},decorate_queries=(e,o)=>{if(!o)return[];const[t,r]=generateFinalQueries(o);for(const[o,r]of t)r.ngMetadataName&&(0,map[r.ngMetadataName])(r.selector,r)(e.prototype,o);return r};var func_extract_forward_ref=__webpack_require__(285);const to_existing_provider=(e,o)=>({provide:e,useExisting:o}),to_factory_provider=(e,o)=>({multi:!0,provide:e,useFactory:o}),processTokens=(e,o)=>{const t=(0,func_get_type.A)(o);return core_form.NG_VALIDATORS&&t===core_form.NG_VALIDATORS?to_factory_provider(t,(()=>new MockValidatorProxy(e))):core_form.NG_ASYNC_VALIDATORS&&t===core_form.NG_ASYNC_VALIDATORS?to_factory_provider(t,(()=>new MockAsyncValidatorProxy(e))):core_form.NG_VALUE_ACCESSOR&&t===core_form.NG_VALUE_ACCESSOR?to_factory_provider(t,(()=>new MockControlValueAccessorProxy(e))):void 0},processOwnUseExisting=(e,o,t)=>{const r=(0,func_get_type.A)(t);if(r!==core_form.NgControl&&r!==core_form.FormControlDirective)return t!==r&&(0,func_extract_forward_ref.A)(t.useExisting)===e?to_existing_provider(r,o):void 0},processProvider=(e,o,t,r)=>{const n=processTokens(o,t);if(n)return n;return processOwnUseExisting(e,o,t)||helper_mock_service.A.resolveProvider(t,r)},clone_providers=(e,o,t,r)=>{const n=[];let s;for(const c of(0,core_helpers.Bq)(t||[])){(0,func_get_type.A)(c)===core_form.NG_VALUE_ACCESSOR&&(s=!1);const t=processProvider(e,o,c,r);t&&n.push(t)}return{providers:n,setControlValueAccessor:s}},buildConfig=(e,o,t)=>({config:ng_mocks_universe.A.config.get(e),outputs:o.outputs,queryScanKeys:[],setControlValueAccessor:t}),decorate_declaration=(e,o,t,r)=>{const n=ng_mocks_universe.A.config.has("mockNgDefResolver");n||ng_mocks_universe.A.config.set("mockNgDefResolver",new core_def_stack);const s=Object.assign({},r);if(void 0!==t.exportAs&&(s.exportAs=t.exportAs),void 0!==t.selector&&(s.selector=t.selector),void 0!==t.standalone&&(s.standalone=t.standalone),t.standalone&&t.imports){const[,{imports:e}]=mock_ng_def({imports:t.imports,skipExports:!0});(null==e?void 0:e.length)&&(s.imports=e)}if(t.hostDirectives){const[,{hostDirectives:e}]=mock_ng_def({hostDirectives:t.hostDirectives,skipExports:!0});(null==e?void 0:e.length)&&(s.hostDirectives=e)}const{setControlValueAccessor:c,providers:_}=clone_providers(e,o,t.providers||[],ng_mocks_universe.A.config.get("mockNgDefResolver"));_.push(to_existing_provider(e,o)),s.providers=_;const{providers:i}=clone_providers(e,o,t.viewProviders||[],ng_mocks_universe.A.config.get("mockNgDefResolver"));i.length>0&&(s.viewProviders=i);const a=buildConfig(e,t,null!=c?c:-1!==helper_mock_service.A.extractMethodsFromPrototype(e.prototype).indexOf("writeValue"));decorate_mock(o,e,a),t.queries&&decorate_inputs(o,t.inputs,Object.keys(t.queries)),decorate_outputs(o,t.outputs),a.queryScanKeys=decorate_queries(o,t.queries),a.hostBindings=[];for(const[e]of t.hostBindings||[])-1===a.hostBindings.indexOf(e)&&a.hostBindings.push(e);a.hostListeners=[];for(const[e]of t.hostListeners||[])-1===a.hostListeners.indexOf(e)&&a.hostListeners.push(e);return n||ng_mocks_universe.A.config.delete("mockNgDefResolver"),s},getType=e=>(0,func_is_ng_def.p)(e,"p")?"pipe":(0,func_is_ng_def.p)(e,"d")?"directive":(0,func_is_ng_def.p)(e,"c")?"component":(0,func_is_ng_def.p)(e,"m")?"module":(0,func_is_ng_def.p)(e,"i")?"service":(0,func_is_ng_def.p)(e,"t")?"token":"",func_import_exists=(e,o)=>{if(null==e)throw new Error(`null / undefined has been passed into ${o}. Please check that its import is correct.`);if("MockPipe"===o&&(0,func_is_ng_def.p)(e,"p"))return;if("MockDirective"===o&&(0,func_is_ng_def.p)(e,"d"))return;if("MockComponent"===o&&(0,func_is_ng_def.p)(e,"c"))return;if("MockModule"===o&&(0,func_is_ng_def.p)(e,"m"))return;const t=getType(e);if(t&&"MockPipe"===o)throw new Error(`${o} accepts pipes, whereas ${(0,func_get_name.A)(e)} is a ${t}.`);if(t&&"MockDirective"===o)throw new Error(`${o} accepts directives, whereas ${(0,func_get_name.A)(e)} is a ${t}.`);if(t&&"MockComponent"===o)throw new Error(`${o} accepts components, whereas ${(0,func_get_name.A)(e)} is a ${t}.`);if(t&&"MockModule"===o)throw new Error(`${o} accepts modules, whereas ${(0,func_get_name.A)(e)} is a ${t}.`)};function isMockNgDef(e,o){return!!e.mockOf&&(!o||(0,func_is_ng_def.p)(e.mockOf,o))}const func_get_last_fixture=()=>{const e=(0,testing_.getTestBed)()._activeFixtures;return e[e.length-1]},return_cached_mock=e=>{let o;try{o=func_get_last_fixture().debugElement.injector.get(core_tokens.en).get(e)}catch(e){}return o||(o=ng_mocks_universe.A.cacheDeclarations.get(e)),e.__ngMocksResolutions&&ng_mocks_universe.A.config.has("mockNgDefResolver")&&ng_mocks_universe.A.config.get("mockNgDefResolver").merge(e.__ngMocksResolutions),o},get_mock=(e,o,t,r,n,s)=>{if(func_import_exists(e,t),isMockNgDef(e,o))return e;if(ng_mocks_universe.A.flags.has(r)&&ng_mocks_universe.A.cacheDeclarations.has(e))return return_cached_mock(e);const c=ng_mocks_universe.A.config.has("ngMocksDepsResolution");c||ng_mocks_universe.A.config.set("ngMocksDepsResolution",new Map);const _=(0,core_helpers.He)(n);return s(e,_),ng_mocks_universe.A.flags.has(r)&&ng_mocks_universe.A.cacheDeclarations.set(e,_),c||ng_mocks_universe.A.config.delete("ngMocksDepsResolution"),_},vcrArgs={read:core_.ViewContainerRef,static:!1},trArgs={read:core_.TemplateRef,static:!1},viewChildTemplate=(e,o)=>`
    `,isTemplateRefQuery=e=>!(e.isViewQuery||e.read&&e.read!==core_.TemplateRef||"string"!=typeof e.selector&&!e.read),generate_template=e=>{const o=[""];if(!e)return o.join("");for(const t of Object.keys(e)){const r=e[t];if(0!==t.indexOf("__mock")&&isTemplateRefQuery(r)){if("string"==typeof r.selector){const t=r.selector.replace(new RegExp("\\W","mg"),"_");e[`__vcrIf_key_${t}`]=new core_.ViewChild(`ngIf_key_${t}`,vcrArgs),e[`__trIf_key_${t}`]=new core_.ViewChild(`ngIf_key_${t}`,trArgs),e[`__mockView_key_${t}`]=new core_.ViewChild(`key_${t}`,vcrArgs),e[`__mockTpl_key_${t}`]=r,o.push(viewChildTemplate(t,"key"))}e[`__vcrIf_prop_${t}`]=new core_.ViewChild(`ngIf_prop_${t}`,vcrArgs),e[`__trIf_prop_${t}`]=new core_.ViewChild(`ngIf_prop_${t}`,trArgs),e[`__mockView_prop_${t}`]=new core_.ViewChild(`prop_${t}`,vcrArgs),o.push(viewChildTemplate(t,"prop"))}}return o.join("")},get_key=e=>{if("string"==typeof e)return["key",`__mockTpl_key_${e}`,e,void 0];const[o,...t]=e;return["prop",o,o,t.length>0?t:void 0]},mixRenderPrepareVcr=(e,o,t,r)=>{const n=e[`__vcrIf_${o}_${t}`],s=e[`__trIf_${o}_${t}`];return n&&s&&!e[`ngMocksRender_${o}_${t}`]&&(e[`ngMocksRender_${o}_${t}`]=n.createEmbeddedView(s,{}),r.detectChanges()),e[`__mockView_${o}_${t}`]},mixRenderReorderViews=(e,o,t)=>{for(const e of o.splice(t+1))e.destroy();let r=0;for(const t of o)t&&(e.move(t,r),r+=1)},mixRenderApplyContext=(e,o)=>{for(const o of Object.keys(e.context))e.context[o]=void 0;for(const t of Object.keys(o))e.context[t]=o[t];e.markForCheck()},mixRenderHandleViews=(e,o,t,r,n,s)=>{let c=-1;for(const o of t)if(c+=1,r[c]=r[c]||void 0,(!n||-1!==n.indexOf(c))&&o){if(!(o instanceof core_.TemplateRef))throw new Error("Cannot find TemplateRef");r[c]||(r[c]=e.createEmbeddedView(o,{})),mixRenderApplyContext(r[c],s)}return o.detectChanges(),c},mixRender=(e,o)=>{(0,core_define_property.A)(e,"__render",((t,r,n)=>{const[s,c,_,i]=get_key(t),a=mixRenderPrepareVcr(e,s,_,o);if(!a)return;const l=e[c],p=l instanceof core_.QueryList?l.toArray():[l],u=e[`ngMocksRender_${s}_${_}_views`]||[],d=mixRenderHandleViews(a,o,p,u,i,Object.assign(Object.assign({},n),{$implicit:r}));mixRenderReorderViews(a,u,d),e[`ngMocksRender_${s}_${_}_views`]=u,o.detectChanges()}))},mixHideHandler=(e,o,t,r)=>{const n=e[`ngMocksRender_${o}_${t}_views`];let s=-1;for(const e of n)s+=1,r&&-1===r.indexOf(s)||!e||(e.destroy(),n[s]=void 0)},mixHide=(e,o)=>{(0,core_define_property.A)(e,"__hide",(t=>{const[r,,n,s]=get_key(t);e[`ngMocksRender_${r}_${n}`]&&(mixHideHandler(e,r,n,s),s||(e[`ngMocksRender_${r}_${n}`].destroy(),e[`ngMocksRender_${r}_${n}`]=void 0),o.detectChanges())}))};class ComponentMockBase extends LegacyControlValueAccessor{constructor(e,o,t){super(e,o),func_is_mock(this)&&(mixRender(this,t),mixHide(this,t))}ngAfterViewInit(){const e=this.__ngMocksConfig.config;if(!this.__rendered&&e&&e.render){for(const o of Object.keys(e.render)){const{$implicit:t,variables:r}=!0===e.render[o]?{$implicit:void 0,variables:{}}:e.render[o];this.__render(o,t,r)}this.__rendered=!0}}}(0,core_define_property.A)(ComponentMockBase,"parameters",[[core_.Injector],[core_form.NgControl||(()=>{}),new core_.Optional,new core_.Self],[core_.ChangeDetectorRef]]);const decorateClass=(e,o)=>{const t=core_reflect_directive_resolve(e);(0,core_.Component)(decorate_declaration(e,o,t,{template:generate_template(t.queries)}))(o)};function MockComponents(...e){return e.map(MockComponent)}function MockComponent(e){return get_mock(e,"c","MockComponent","cacheComponent",ComponentMockBase,decorateClass)}class DirectiveMockBase extends LegacyControlValueAccessor{constructor(e,o,t,r,n=null,s=null){super(e,o),this.__ngMocksInstall(r,t,n,s)}ngOnInit(){const e=this.__ngMocksConfig.config;if(null==e?void 0:e.render){const{$implicit:o,variables:t}=!0===e.render?{$implicit:void 0,variables:{}}:e.render;this.__render(o,t)}}__ngMocksInstall(e,o,t,r){(0,core_define_property.A)(this,"__element",t),(0,core_define_property.A)(this,"__template",r),(0,core_define_property.A)(this,"__viewContainer",e),(0,core_define_property.A)(this,"__vcr",e),(0,core_define_property.A)(this,"__cdr",o),(0,core_define_property.A)(this,"__isStructural",r&&e),(0,core_define_property.A)(this,"__render",((t,n)=>{e&&r&&(e.clear(),e.createEmbeddedView(r,Object.assign(Object.assign({},n),{$implicit:t})),o.detectChanges())}))}}(0,core_define_property.A)(DirectiveMockBase,"parameters",[[core_.Injector],[core_form.NgControl||(()=>{}),new core_.Optional,new core_.Self],[core_.ChangeDetectorRef],[core_.ViewContainerRef],[core_.ElementRef,new core_.Optional,new core_.Self],[core_.TemplateRef,new core_.Optional,new core_.Self]]);const mock_directive_decorateClass=(e,o)=>{const t=core_reflect_directive_resolve(e),r=decorate_declaration(e,o,t,{});(0,core_.Directive)(r)(o)};function MockDirectives(...e){return e.map(MockDirective)}function MockDirective(e){return get_mock(e,"d","MockDirective","cacheDirective",DirectiveMockBase,mock_directive_decorateClass)}const core_reflect_pipe_resolve=e=>core_reflect_body_catch((e=>{const o=(0,collect_declarations.A)(e);if(o.Pipe)return o.Pipe;throw new Error("Cannot resolve declarations")}))(e);function MockPipes(...e){return e.map((e=>MockPipe(e,void 0)))}const getMockClass=(e,o)=>{var t;const r=ng_mocks_universe.A.config.get(e),n=null!=o?o:null===(t=null==r?void 0:r.defValue)||void 0===t?void 0:t.transform,s=(0,core_helpers.He)(Mock);return(0,core_.Pipe)(core_reflect_pipe_resolve(e))(s),decorate_mock(s,e,{init:e=>{n&&(e.transform=n),e.transform||helper_mock_service.A.mock(e,"transform",`${(0,func_get_name.A)(e)}.transform`)},transform:n}),s};function MockPipe(e,o){if(func_import_exists(e,"MockPipe"),isMockNgDef(e,"p"))return e;if(ng_mocks_universe.A.flags.has("cachePipe")&&ng_mocks_universe.A.cacheDeclarations.has(e))return return_cached_mock(e);const t=getMockClass(e,o);return ng_mocks_universe.A.flags.has("cachePipe")&&ng_mocks_universe.A.cacheDeclarations.set(e,t),t}const flagMock=e=>"mock"===e&&ng_mocks_universe.A.flags.has("skipMock"),flagKeep=e=>"keep"===e&&!ng_mocks_universe.A.flags.has("skipMock"),flagReplace=e=>"replace"===e&&!ng_mocks_universe.A.flags.has("skipMock"),flagNever=e=>-1!==core_config.A.neverMockModule.indexOf((0,func_get_name.A)(e))&&!ng_mocks_universe.A.flags.has("skipMock"),preProcessFlags=e=>{let o=!1,t=!0;ng_mocks_universe.A.flags.has("hasRootModule")?t=!1:ng_mocks_universe.A.flags.add("hasRootModule");const r=ng_mocks_universe.A.getResolution(e);return flagMock(r)&&(o=!0,ng_mocks_universe.A.flags.delete("skipMock")),flagNever(e)&&(o=!0,ng_mocks_universe.A.flags.add("skipMock")),t||!flagKeep(r)&&!flagReplace(r)||(o=!0,ng_mocks_universe.A.flags.add("skipMock")),{isRootModule:t,toggleSkipMockFlag:o}},postProcessFlags=({isRootModule:e,toggleSkipMockFlag:o})=>{o&&ng_mocks_universe.A.flags.has("skipMock")?ng_mocks_universe.A.flags.delete("skipMock"):o&&!ng_mocks_universe.A.flags.has("skipMock")&&ng_mocks_universe.A.flags.add("skipMock"),e&&ng_mocks_universe.A.flags.delete("hasRootModule")},extractModuleAndProviders=e=>{let o,t;return(0,func_is_ng_module_def_with_providers.h)(e)?(o=e.ngModule,e.providers&&(t=e.providers)):o=e,{ngModule:o,ngModuleProviders:t}},getExistingMockModule=(e,o)=>{var t;if(isMockNgDef(e,"m"))return e;if(ng_mocks_universe.A.flags.has("cacheModule")&&ng_mocks_universe.A.cacheDeclarations.has(e))return return_cached_mock(e);if(!o&&"mock"!==(null===(t=ng_mocks_universe.A.config.get("ngMocksDepsResolution"))||void 0===t?void 0:t.get(e))&&ng_mocks_universe.A.hasBuildDeclaration(e)){const o=ng_mocks_universe.A.getBuildDeclaration(e);if((0,func_is_ng_def.p)(o,"m")&&o!==e)return o}},detectMockModule=(e,o)=>{const[t,r,n]=o?[!1]:mock_ng_def(core_reflect_module_resolve(e),e);if(n&&(0,core_define_property.A)(e,"__ngMocksResolutions",n),t){const o=ng_mocks_universe.A.flags.has("skipMock")?e:Mock,t=(0,core_helpers.He)(o);return(0,core_.NgModule)(r)(t),decorate_mock(t,e),t}return o||e},getMockProviders=e=>{if(e){const[o,t]=mock_ng_def({providers:e,skipExports:!0});return o?t.providers:e}},generateReturn=(e,o,t,r,n)=>r===o&&n===t?e:(0,func_is_ng_module_def_with_providers.h)(e)?Object.assign({ngModule:r},n?{providers:n}:{}):r;function MockModule(e){var o;func_import_exists(e,"MockModule");const{ngModule:t,ngModuleProviders:r}=extractModuleAndProviders(e),n=preProcessFlags(t);try{const s=detectMockModule(t,getExistingMockModule(t,n.isRootModule));ng_mocks_universe.A.flags.has("cacheModule")&&ng_mocks_universe.A.cacheDeclarations.set(t,s),ng_mocks_universe.A.flags.has("skipMock")&&(null===(o=ng_mocks_universe.A.config.get("ngMocksDepsSkip"))||void 0===o||o.add(s));const c=getMockProviders(r);return generateReturn(e,t,r,s,c)}finally{postProcessFlags(n)}}const processDefMap=[["c",MockComponent],["d",MockDirective],["p",MockPipe]],processDef=e=>{if((0,func_is_ng_def.p)(e,"m")||(0,func_is_ng_module_def_with_providers.h)(e))return MockModule(e);if(ng_mocks_universe.A.hasBuildDeclaration(e))return ng_mocks_universe.A.getBuildDeclaration(e);if(ng_mocks_universe.A.flags.has("skipMock")&&"mock"!==ng_mocks_universe.A.getResolution(e))return e;for(const[o,t]of processDefMap)if((0,func_is_ng_def.p)(e,o))return t(e)},createResolveProvider=(e,o)=>t=>helper_mock_service.A.resolveProvider(t,e,o),createResolveWithProviders=(e,o)=>(0,func_is_ng_module_def_with_providers.h)(o)&&(0,func_is_ng_module_def_with_providers.h)(e),createResolveExisting=(e,o,t)=>{const r=o.get(e);return e!==r&&t(),r},createResolveExcluded=(e,o,t)=>{o.set(e,void 0),t()},createResolve=(e,o)=>t=>{var r;if(e.has(t))return createResolveExisting(t,e,o);const n=(0,func_get_type.A)(t);if(ng_mocks_universe.A.isExcludedDef(n))return createResolveExcluded(t,e,o);ng_mocks_universe.A.touches.add(n);const s=processDef(t);return createResolveWithProviders(t,s)&&e.set(t.ngModule,s.ngModule),ng_mocks_universe.A.flags.has("skipMock")&&(null===(r=ng_mocks_universe.A.config.get("ngMocksDepsSkip"))||void 0===r||r.add(s)),e.set(t,s),o(s!==t),s},create_resolvers=(e,o)=>({resolve:createResolve(o,e),resolveProvider:createResolveProvider(o,e)}),mark_providers=e=>{for(const o of(0,core_helpers.Bq)(null!=e?e:[])){const e=(0,func_get_type.A)(o);mark_exported(e)}},flatToExisting=(e,o)=>(0,core_helpers.Bq)(e).map(o).filter((e=>!!e)),configureProcessMetaKeys=(e,o)=>[["declarations",e],["hostDirectives",o=>{const t=(0,func_get_type.A)(o),r=e(t);return r===t?o:o==t?r:Object.assign(Object.assign({},o),{directive:r})}],["imports",e],["entryComponents",e],["bootstrap",e],["providers",o],["viewProviders",o],["exports",e],["schemas",e=>e]],processMeta=(e,o,t)=>{var r;const n={},s=configureProcessMetaKeys(o,t),c=ng_mocks_universe.A.flags.has("cachePipe");c||ng_mocks_universe.A.flags.add("cachePipe");for(const[o,t]of s)(null===(r=e[o])||void 0===r?void 0:r.length)&&(n[o]=flatToExisting(e[o],t));return e.skipMarkProviders||(mark_providers(n.providers),mark_providers(n.viewProviders)),c||ng_mocks_universe.A.flags.delete("cachePipe"),n},resolveDefForExport=(e,o,t,r)=>{const n=ng_mocks_universe.A.config.get(r)||{},s=(0,func_get_type.A)(e),c=o(s);if(!c)return;const _=ng_mocks_universe.A.config.get(s);return(null==_?void 0:_.export)&&r&&!n.export&&ng_mocks_universe.A.config.set(r,Object.assign(Object.assign({},n),{export:!0})),!t||n.exportAll||(null==_?void 0:_.export)?(mark_exported(s,r),c):void 0},skipAddExports=(e,o)=>!e||!!o.exports&&-1!==o.exports.indexOf(e),addExports=(e,o,t,r,n)=>{const s=ng_mocks_universe.A.flags.has("skipMock")||ng_mocks_universe.A.flags.has("correctModuleExports");for(const c of(0,core_helpers.Bq)([t.imports||[],t.declarations||[]])){const t=resolveDefForExport(c,e,s,n);skipAddExports(t,r)||(o(),r.exports=r.exports||[],r.exports.push(t))}},mock_ng_def=(e,o)=>{const t=ng_mocks_universe.A.config.has("mockNgDefResolver");t||ng_mocks_universe.A.config.set("mockNgDefResolver",new core_def_stack),ng_mocks_universe.A.config.get("mockNgDefResolver").push();let r=!ng_mocks_universe.A.flags.has("skipMock");const n=(e=!0)=>{r=r||e},{resolve:s,resolveProvider:c}=create_resolvers(n,ng_mocks_universe.A.config.get("mockNgDefResolver")),_=processMeta(e,s,c);e.skipExports||addExports(s,n,e,_,o);for(const e of o&&_.exports?(0,core_helpers.Bq)(_.exports):[])mark_exported(e,o);const i=ng_mocks_universe.A.config.get("mockNgDefResolver").pop();return t||ng_mocks_universe.A.config.delete("mockNgDefResolver"),[r,_,i]},get_override_def=e=>{if(!e)return;const o=ng_mocks_universe.A.flags.has("skipMock");o||ng_mocks_universe.A.flags.add("skipMock");const[t,r]=mock_ng_def(e);return o||ng_mocks_universe.A.flags.delete("skipMock"),t?r:void 0},skip_override=(e,o,t,r)=>!!ng_mocks_universe.A.cacheDeclarations.has(r)||!(!e.has(t)||r!==o.get(t)),create_ng_mocks_overrides_token=(e,o)=>{const t=new Map;for(const r of(0,core_helpers.LG)(ng_mocks_universe.A.touches)){const n=r,s=ng_mocks_universe.A.getBuildDeclaration(n)||n;if(skip_override(e,o,n,s))continue;const c=core_reflect_meta(s),_=get_override_def(c);if(!_)continue;const i={};for(const e of Object.keys(_))i[e]=c[e];t.set(s,[{set:_},{set:i}])}return{provide:core_tokens.So,useValue:t}},create_ng_mocks_token=()=>{const e=new Map;for(const[o,t]of[...(0,core_helpers.Nn)(ng_mocks_universe.A.builtProviders),...(0,core_helpers.Nn)(ng_mocks_universe.A.builtDeclarations),...(0,core_helpers.Nn)(ng_mocks_universe.A.cacheDeclarations),...(0,core_helpers.Nn)(ng_mocks_universe.A.cacheProviders)])e.has(o)||e.set(o,t);return{provide:core_tokens.en,useValue:e}},create_ng_mocks_touches_token=()=>{const e=new Set;for(const o of(0,core_helpers.LG)(ng_mocks_universe.A.touches)){const t=o;let r=ng_mocks_universe.A.getBuildDeclaration(t);void 0===r&&(r=t),e.add(t),e.add(r)}return{provide:core_tokens.Em,useValue:e}};var helper_create_clone=__webpack_require__(365);class EntryComponentsModule{constructor(e,o){if(!o)return;const t=o.resolveComponentFactory;o.resolveComponentFactory=(0,helper_create_clone.A)(t,void 0,void 0,((r,...n)=>{var s;return t.apply(o,[null!==(s=e.get(r))&&void 0!==s?s:r,...n])}))}}(0,core_define_property.A)(EntryComponentsModule,"parameters",[[core_tokens.en],[core_.ComponentFactoryResolver,new core_.Optional]]);class IvyModule{}(0,core_.NgModule)()(IvyModule);const handle_entry_components=e=>{const o=[];for(const t of e.declarations)(0,func_is_ng_def.p)(t,"c")&&o.push(t);const t=(0,core_helpers.He)(EntryComponentsModule);(0,core_.NgModule)({entryComponents:IvyModule.ɵmod?[]:o})(t),e.imports.push(t)};var helper_use_factory=__webpack_require__(465),core_reflect_parameters_resolve=__webpack_require__(749),common_x=e=>{var o={};return __webpack_require__.d(o,e),o},common_y=e=>()=>e;const common_namespaceObject=common_x({DOCUMENT:()=>__WEBPACK_EXTERNAL_MODULE__angular_common_d12e0fe1__.DOCUMENT}),skipResolution=e=>{const o=ng_mocks_universe.A.getResolution(e);return"keep"===o||"exclude"===o||"mock"!==o&&void 0},skipSystem=e=>{if(!e||e===common_namespaceObject.DOCUMENT||ng_mocks_universe.A.touches.has(e))return!0;const o=skipResolution(e);return void 0!==o?o:"function"==typeof e&&-1!==core_config.A.neverMockProvidedFunction.indexOf(e.name)||!(!(0,func_is_ng_injection_token.S)(e)||-1===core_config.A.neverMockToken.indexOf(e.toString()))},skip_dep=e=>{if(skipSystem(e))return!0;const o=core_reflect_provided_in(e);return!("function"!=typeof e||o&&"platform"!==o)},add_def_to_root_provider_parameters=(e,o,t)=>{skip_dep(t)||!o.has(core_tokens.gG)&&ng_mocks_universe.A.config.get("ngMocksDepsSkip").has(t)||e.add(t)},check_root_provider_dependency=(e,o,t)=>{"function"==typeof e&&-1===t.indexOf(e)&&(t.push(e),o.push(e))},extract_dep=e=>{if(!e)return;let o;for(const t of e)t&&"object"==typeof t&&t.token&&(o=t.token),o||!t||"object"==typeof t&&t.ngMetadataName||(o=t);return(0,func_extract_forward_ref.A)(o)},get_root_providers_data=()=>{const e=[(0,core_helpers.LG)(ng_mocks_universe.A.config.get("ngMocksDepsSkip")),(0,core_helpers.LG)(ng_mocks_universe.A.config.get("ngMocksDeps")),(0,core_helpers.LG)(ng_mocks_universe.A.touches)];return{buckets:e,touched:[].concat(...e)}},handle_provided_in_dependency=e=>{if(ng_mocks_universe.A.touches.has(e))return;const o=core_reflect_provided_in(e);o&&ng_mocks_universe.A.config.get("ngMocksDepsSkip").has(o)&&ng_mocks_universe.A.config.get("ngMocksDepsSkip").add(e)},skip_root_provider_dependency=e=>!!skip_dep(e)||ng_mocks_universe.A.config.get("ngMocksDepsSkip").has(e),get_root_provider_parameters=e=>{const o=new Set,{buckets:t,touched:r}=get_root_providers_data();for(const n of t)for(const t of n){add_def_to_root_provider_parameters(o,e,t);for(const s of(0,core_reflect_parameters_resolve.A)(t)){const c=extract_dep(s);handle_provided_in_dependency(c),skip_root_provider_dependency(c)||(check_root_provider_dependency(c,r,n),e.has(core_tokens.gG)||!ng_mocks_universe.A.config.get("ngMocksDepsSkip").has(t)?o.add(c):ng_mocks_universe.A.config.get("ngMocksDepsSkip").add(c))}}return o},handle_root_providers=(e,{keepDef:o,mockDef:t},r)=>{const n=o.has(core_tokens.gG)?new Set:get_root_provider_parameters(t);if(n.size>0)for(const o of(0,core_helpers.LG)(n)){const t=(0,helper_resolve_provider.A)(o,r);if(t)e.providers.push(t);else if((0,func_is_ng_injection_token.S)(o)){const t=ng_mocks_universe.A.config.has("ngMocksMulti")&&ng_mocks_universe.A.config.get("ngMocksMulti").has(o);e.providers.push((0,helper_use_factory.A)(o,(()=>t?[]:void 0)))}}},init_module=(e,o)=>{var t;const r=null!==(t=ng_mocks_universe.A.config.get("mockNgDefResolver").get(e))&&void 0!==t?t:ng_mocks_universe.A.getBuildDeclaration(e),n=o.has(e)?o.get(e):void 0;return n?{ngModule:r,providers:n}:r},skipDef=e=>ng_mocks_universe.A.touches.has(e)||(0,func_is_ng_def.p)(e)||(0,func_is_ng_injection_token.S)(e)||"string"==typeof e,handleDef=({imports:e,declarations:o,providers:t},r,n)=>{skipDef(r)||error_jest_mock(r);let s=!1;if((0,func_is_ng_def.p)(r,"m")){const o=init_module(r,n);if(e.push(o),s=!0,"object"==typeof o&&o.providers)for(const e of(0,core_helpers.Bq)(o.providers))ng_mocks_universe.A.touches.add((0,func_get_type.A)(e))}if(((0,func_is_ng_def.p)(r,"c")||(0,func_is_ng_def.p)(r,"d")||(0,func_is_ng_def.p)(r,"p"))&&((isStandalone(r)?e:o).push(ng_mocks_universe.A.getBuildDeclaration(r)),s=!0),(0,func_is_ng_def.p)(r,"i")||!(0,func_is_ng_def.p)(r)){const e=ng_mocks_universe.A.builtProviders.get(r);e&&"string"!=typeof e&&!1===(0,func_is_ng_def.p)(e,"t")&&(t.push(e),s=!0)}s&&(mark_exported(r),ng_mocks_universe.A.touches.add(r))},isExportedOnRoot=(e,o,t)=>{const r=o.get(e),n=t.get(e)||{};if((0,func_is_ng_def.p)(e,"m")&&n.onRoot)return e;if(!(null==r?void 0:r.exported))return e;for(const e of(0,core_helpers.LG)(r.exported)){const r=isExportedOnRoot(e,o,t);if(r)return r}},moveModulesUp=(e,o)=>{const t=(0,func_is_ng_def.p)(e,"m"),r=(0,func_is_ng_def.p)(o,"m");return t&&r?0:t?-1:r?1:0},init_ng_modules=({configDefault:e,keepDef:o,mockDef:t,replaceDef:r},n)=>{const s={imports:[],declarations:[],providers:[]},c=[],_=[],i=[...(0,core_helpers.LG)(t),...(0,core_helpers.LG)(o),...(0,core_helpers.LG)(r)];i.sort(moveModulesUp);for(const o of i){const t=(0,func_is_ng_def.p)(o,"m")&&n.has(o)?o:isExportedOnRoot(o,ng_mocks_universe.A.configInstance,ng_mocks_universe.A.config);if(!t||-1!==c.indexOf(t))continue;const r=ng_mocks_universe.A.config.get(t)||{__set:!0};c.push(t),r.onRoot=r.onRoot||!r.dependency,r.__set&&(r.__set=void 0,ng_mocks_universe.A.config.set(t,r)),(0,func_is_ng_def.p)(t,"m")&&r.onRoot?handleDef(s,t,n):r.dependency||!r.export||!(0,func_is_ng_def.p)(t,"i")&&(0,func_is_ng_def.p)(t)?!r.dependency&&r.export?handleDef(s,t,n):ng_mocks_universe.A.touches.has(t)||r.dependency?!r.dependency||!e.dependency||"root"===core_reflect_provided_in(t)||"object"==typeof t&&t.__ngMocksSkip||_.push(t):handleDef(s,t,n):(handleDef(s,t,n),mark_providers([t]))}const a=ng_mocks_universe.A.global.get("flags");for(const e of _){if(ng_mocks_universe.A.touches.has(e))continue;const o=[`MockBuilder has found a missing dependency: ${(0,func_get_name.A)(e)}.`,"It means no module provides it.",'Please, use the "export" flag if you want to add it explicitly.',"https://ng-mocks.sudo.eu/api/MockBuilder#export-flag"].join(" ");if("warn"===a.onMockBuilderMissingDependency)console.warn(o);else if("throw"===a.onMockBuilderMissingDependency)throw new Error(o)}return s},funcExtractDeps=(e,o,t=!1)=>{const r=(0,collect_declarations.A)(e),n=getNgType(e);if(!n||"Injectable"===n)return o;const s=r[n];for(const e of core_config.A.dependencies)if(s[e])for(const r of(0,core_helpers.Bq)(s[e])){const e=(0,func_get_type.A)(r);o.has(e)||(o.add(e),t&&funcExtractDeps(e,o))}return o},init_exclude_def=e=>{const o=ng_mocks_universe.A.builtDeclarations,t=ng_mocks_universe.A.builtProviders,r=ng_mocks_universe.A.config.get("ngMocksDepsResolution");for(const n of(0,core_helpers.LG)(e))o.set(n,null),t.set(n,null),r.set(n,"exclude")},init_keep_def=(e,o)=>{const t=new Set,r=ng_mocks_universe.A.builtDeclarations,n=ng_mocks_universe.A.builtProviders,s=ng_mocks_universe.A.config.get("ngMocksDepsResolution");for(const c of(0,core_helpers.LG)(e))r.set(c,c),n.set(c,c),s.set(c,"keep"),o.get(c).shallow&&funcExtractDeps(c,t);return t};var mock_provider=__webpack_require__(415);const createInstance=(e,o,t)=>t.precise?o:(0,mock_helper_stub.A)(e,o),try_mock_provider=(e,o)=>{if((0,func_is_ng_def.p)(e,"i")&&o.has(e)){const t=ng_mocks_universe.A.config.get(e),r=o.get(e);ng_mocks_universe.A.builtProviders.set(e,(0,helper_use_factory.A)(e,void 0,(e=>createInstance(e,r,t))))}else(0,func_is_ng_def.p)(e,"i")&&ng_mocks_universe.A.builtProviders.set(e,(0,mock_provider.A)(e,!0));if(!(0,func_is_ng_def.p)(e)&&o.has(e)){const t=o.get(e);ng_mocks_universe.A.builtProviders.set(e,(0,helper_use_factory.A)(e,void 0,(()=>t)))}else(0,func_is_ng_def.p)(e)||ng_mocks_universe.A.builtProviders.set(e,(0,mock_provider.A)(e,!0))},init_mock_declarations=(e,o)=>{const t=ng_mocks_universe.A.builtDeclarations,r=ng_mocks_universe.A.config.get("ngMocksDepsResolution");for(const n of(0,core_helpers.LG)(e)){const e=!ng_mocks_universe.A.touches.has(n);r.set(n,"mock"),t.set(n,void 0),try_mock_provider(n,o),e&&ng_mocks_universe.A.touches.delete(n)}},try_mock_declaration=e=>{void 0===ng_mocks_universe.A.builtDeclarations.get(e)&&((0,func_is_ng_def.p)(e,"c")&&ng_mocks_universe.A.builtDeclarations.set(e,MockComponent(e)),(0,func_is_ng_def.p)(e,"d")&&ng_mocks_universe.A.builtDeclarations.set(e,MockDirective(e)),(0,func_is_ng_def.p)(e,"p")&&ng_mocks_universe.A.builtDeclarations.set(e,MockPipe(e)))},init_modules=(e,o,t,r)=>{var n,s,c,_,i;const a=new Map;for(const l of[...(0,core_helpers.LG)(e),...(0,core_helpers.LG)(o),...(0,core_helpers.LG)(t)]){const e=(0,collect_declarations.A)(l),t=[...null!==(n=r.get(l))&&void 0!==n?n:[],...null!==(c=null===(s=e.Component)||void 0===s?void 0:s.providers)&&void 0!==c?c:[],...null!==(i=null===(_=e.Directive)||void 0===_?void 0:_.providers)&&void 0!==i?i:[]],p=!ng_mocks_universe.A.touches.has(l);o.has(l)||ng_mocks_universe.A.flags.add("skipMock");const u=(0,func_is_ng_def.p)(l,"m");if(t.length>0){const[,e]=mock_ng_def({providers:t,skipMarkProviders:!u,skipExports:!0});a.set(l,e.providers)}u&&ng_mocks_universe.A.builtDeclarations.set(l,MockModule(l)),ng_mocks_universe.A.flags.delete("skipMock"),p&&ng_mocks_universe.A.touches.delete(l)}for(const e of(0,core_helpers.LG)(o))try_mock_declaration(e);return a},init_replace_def=(e,o)=>{const t=ng_mocks_universe.A.builtDeclarations,r=ng_mocks_universe.A.config.get("ngMocksDepsResolution");for(const n of(0,core_helpers.LG)(e))t.set(n,o.get(n)),r.set(n,"replace")},init_universe=({configDef:e,defProviders:o,defValue:t,excludeDef:r,keepDef:n,mockDef:s,replaceDef:c})=>{ng_mocks_universe.A.flags.add("cachePipe"),ng_mocks_universe.A.config.set("ngMocksMulti",new Set),ng_mocks_universe.A.config.set("ngMocksDeps",new Set),ng_mocks_universe.A.config.set("ngMocksDepsSkip",new Set),ng_mocks_universe.A.config.set("ngMocksDepsResolution",new Map);const _=init_keep_def(n,e);for(const e of(0,core_helpers.LG)(_))ng_mocks_universe.A.touches.add(e);for(const e of(0,core_helpers.LG)(n))_.add(e),funcExtractDeps(e,_,!0);for(const e of(0,core_helpers.LG)(s))_.add(e),funcExtractDeps(e,_,!0);for(const e of(0,core_helpers.LG)(c))_.add(e),funcExtractDeps(e,_,!0);for(const o of(0,core_helpers.LG)(_)){if(e.has(o))continue;const _=ng_mocks_universe.A.getResolution(o);"replace"===_?(c.add(o),t.set(o,ng_mocks_universe.A.getBuildDeclaration(o))):"keep"===_?n.add(o):"exclude"===_?r.add(o):("mock"===_||ng_mocks_universe.A.touches.has(o))&&s.add(o),e.set(o,ng_mocks_universe.A.touches.has(o)?{dependency:!0,__internal:!0}:{})}for(const[o,r]of(0,core_helpers.Nn)(e))ng_mocks_universe.A.config.set(o,Object.assign(Object.assign(Object.assign({},ng_mocks_universe.A.getConfigMock().get(o)),r),{defValue:t.get(o)}));return init_replace_def(c,t),init_exclude_def(r),init_mock_declarations(s,t),init_modules(n,s,c,o)},parse_mock_arguments=(e,o,t,r)=>{let n=e===o?r:o,s=null!=t?t:o!==r&&"object"==typeof o?o:void 0;return(0,func_is_ng_def.p)(e,"p")&&"function"==typeof o&&o!==e&&!(0,func_is_ng_def.p)(o,"p")?(n={transform:o},s=t):!(0,func_is_ng_def.p)(e,"i")&&(0,func_is_ng_def.p)(e)||(s=t),n=n===s?r:n,{config:s,mock:n}},parse_provider=e=>{const o=(0,func_get_type.A)(e);return{multi:o!==e&&e.multi,provide:o}};var __awaiter=function(e,o,t,r){return new(t||(t=Promise))((function(n,s){function c(e){try{i(r.next(e))}catch(e){s(e)}}function _(e){try{i(r.throw(e))}catch(e){s(e)}}function i(e){var o;e.done?n(e.value):(o=e.value,o instanceof t?o:new t((function(e){e(o)}))).then(c,_)}i((r=r.apply(e,o||[])).next())}))};const normaliseModule=e=>(0,func_is_ng_module_def_with_providers.h)(e)?{def:e.ngModule,providers:e.providers}:{def:e,providers:void 0},generateProviderValue=(e,o,t)=>t?[...Array.isArray(o)?o:[],e]:e,defaultMock={};class MockBuilderPromise{constructor(e){this.configDefault=e,this.beforeCC=new Set,this.configDef=new Map,this.defProviders=new Map,this.defValue=new Map,this.excludeDef=new Set,this.keepDef=new Set,this.mockDef=new Set,this.providerDef=new Map,this.replaceDef=new Set,this.stash=new MockBuilderStash,"undefined"!=typeof Symbol&&(this[Symbol.toStringTag]="Promise")}beforeCompileComponents(e){return this.beforeCC.add(e),this}build(){this.stash.backup();const e=new core_def_stack;ng_mocks_universe.A.config.set("mockNgDefResolver",e),ng_mocks_universe.A.flags.add("hasRootModule");try{const o=this.combineParams(),t=init_ng_modules(o,init_universe(o));return add_requested_providers(t,o,e),handle_root_providers(t,o,e),handle_entry_components(t),apply_platform_modules(),t.providers.push(create_ng_mocks_token(),create_ng_mocks_touches_token(),create_ng_mocks_overrides_token(this.replaceDef,this.defValue),MockBuilder),t}finally{ng_mocks_universe.A.flags.delete("hasRootModule"),ng_mocks_universe.A.config.delete("mockNgDefResolver"),this.stash.restore()}}catch(e){return __awaiter(this,void 0,void 0,(function*(){return this.then().catch(e)}))}exclude(e){return this.wipe(e),this.excludeDef.add(e),this.setConfigDef(e),this}finally(e){return __awaiter(this,void 0,void 0,(function*(){return this.then().finally(e)}))}keep(e,o){const{def:t,providers:r}=normaliseModule(e),n=this.keepDef.has(t)?this.defProviders.get(t):[];return this.wipe(t),this.keepDef.add(t),r&&this.defProviders.set(t,[...n||[],...r]),this.setConfigDef(t,o),this}mock(e,o=defaultMock,t){const{def:r,providers:n}=normaliseModule(e),{config:s,mock:c}=parse_mock_arguments(r,o,t,defaultMock);if((0,func_is_ng_def.p)(c)&&(0,func_is_ng_def.p)(e)&&!(0,func_is_ng_def.p)(e,"t"))throw new Error([`MockBuilder.mock(${(0,func_get_name.A)(e)}) received a class when its shape is expected.`,"Please try ngMocks.defaultMock instead."].join(" "));const _=this.mockDef.has(r)?this.defProviders.get(r):[];return this.wipe(r),this.mockDef.add(r),n&&this.defProviders.set(r,[..._||[],...n]),this.setDefValue(r,c),this.setConfigDef(r,s),this}provide(e){for(const o of(0,core_helpers.Bq)(e)){const{provide:e,multi:t}=parse_provider(o),r=this.providerDef.has(e)?this.providerDef.get(e):[];this.providerDef.set(e,generateProviderValue(o,r,t))}return this}replace(e,o,t){if(!(0,func_is_ng_def.p)(o)||!(0,func_is_ng_def.p)(e)||(0,func_is_ng_def.p)(o,"i")||(0,func_is_ng_def.p)(e,"i"))throw new Error("Cannot replace the declaration, both have to be a Module, a Component, a Directive or a Pipe, for Providers use `.mock` or `.provide`");return this.wipe(e),this.replaceDef.add(e),this.defValue.set(e,o),this.setConfigDef(e,t),this}then(e,o){return __awaiter(this,void 0,void 0,(function*(){const t=new Promise((e=>{const o=testing_.TestBed.configureTestingModule(this.build());for(const e of(0,core_helpers.LG)(this.beforeCC))e(o);o.compileComponents().then((()=>{e({testBed:o})}))}));return t.then(e,o)}))}combineParams(){return{configDef:this.configDef,configDefault:this.configDefault,defProviders:this.defProviders,defValue:this.defValue,excludeDef:this.excludeDef,keepDef:this.keepDef,mockDef:this.mockDef,providerDef:this.providerDef,replaceDef:this.replaceDef}}setConfigDef(e,o){!o&&this.configDef.has(e)||this.configDef.set(e,null!=o?o:this.configDefault)}setDefValue(e,o){o===defaultMock?this.defValue.delete(e):this.defValue.set(e,o)}wipe(e){this.defProviders.delete(e),this.defValue.delete(e),this.excludeDef.delete(e),this.keepDef.delete(e),this.mockDef.delete(e),this.providerDef.delete(e),this.replaceDef.delete(e)}}const objectsDiffer=(e,o)=>{const t=Object.keys(e),r=Object.keys(o);if(t.length!==r.length)return!0;for(const r of t)if(e[r]!==o[r])return!0;return!1},equal_variables=(e,o)=>e===o||!(e&&!o||!e&&o)&&!objectsDiffer(e,o),equal_render_defs=(e,o)=>e===o||("boolean"!=typeof e&&"boolean"!=typeof o||e===o)&&e.$implicit===o.$implicit&&equal_variables(e.variables,o.variables),equal_render_configs_objectsDiffer=(e,o)=>{if(Object.keys(e).length!==Object.keys(o).length)return!0;for(const t of Object.keys(e))if(!equal_render_defs(e[t],o[t]))return!0;return!1},equal_render_configs=(e,o)=>!(!equal_render_defs(o,e)||"object"==typeof o&&"object"==typeof e&&equal_render_configs_objectsDiffer(o,e)),are_equal_config_params=(e,o)=>o===e||o.dependency===e.dependency&&o.export===e.export&&o.exportAll===e.exportAll&&!!equal_render_configs(e.render,o.render),are_equal_maps=(e,o,t=((e,o)=>e===o))=>{if(!o||o.size!==e.size)return!1;for(const r of(0,core_helpers.d4)(e)){if(!o.has(r))return!1;if(!t(o.get(r),e.get(r)))return!1}return!0},areEqualProviderDefs=(e,o,...t)=>{for(const r of t)if(o&&e&&o[r]&&e[r]&&o[r]===e[r])return!0;return o===e},are_equal_providers=(e,o)=>{if(Array.isArray(e)!==Array.isArray(o))return!1;const[t,r]=[(0,core_helpers.Bq)(e),(0,core_helpers.Bq)(o)];if(t.length!==r.length)return!1;for(let e=0;e{if(!o||o.size!==e.size)return!1;for(const t of(0,core_helpers.LG)(e))if(!o.has(t))return!1;return!0},get_empty_config=()=>({beforeCC:new Set,configDef:new Map,defProviders:new Map,defValue:new Map,excludeDef:new Set,keepDef:new Set,mockDef:new Set,providerDef:new Map,replaceDef:new Set}),required_metadata=e=>Object.assign(Object.assign({},e),{declarations:[...e.declarations||[]],imports:[...e.imports||[]],providers:[...e.providers||[]]});var mock_builder_performance_awaiter=function(e,o,t,r){return new(t||(t=Promise))((function(n,s){function c(e){try{i(r.next(e))}catch(e){s(e)}}function _(e){try{i(r.throw(e))}catch(e){s(e)}}function i(e){var o;e.done?n(e.value):(o=e.value,o instanceof t?o:new t((function(e){e(o)}))).then(c,_)}i((r=r.apply(e,o||[])).next())}))};class MockBuilderPerformance extends MockBuilderPromise{build(){const e=ng_mocks_universe.A.global;if(e.has("builder:module")&&e.has("builder:config")&&this.equalsTo(e.get("builder:config")))return required_metadata(e.get("builder:module"));e.has("builder:module")&&e.delete(e.get("builder:module"));const o=this.cloneConfig(),t=super.build();return e.set("builder:config",o),e.set("builder:module",t),required_metadata(t)}then(e,o){const t=Object.create(null,{then:{get:()=>super.then}});return mock_builder_performance_awaiter(this,void 0,void 0,(function*(){const r=ng_mocks_universe.A.global;if(r.has("bullet")&&r.has("builder:module")&&r.has("builder:config")&&this.equalsTo(r.get("builder:config")))return r.get(r.get("builder:module")).then(e,o);r.has("bullet")&&r.has("bullet:reset")&&(console.warn("ngMocks.faster has zero effect due to changes in testing module between runs"),r.delete("bullet"),testing_.TestBed.resetTestingModule(),r.set("bullet",!0));const n=t.then.call(this,e,o);return r.set(r.get("builder:module"),n),n}))}cloneConfig(){const e=get_empty_config();return(0,core_helpers.LG)(this.beforeCC,e.beforeCC),(0,core_helpers.LG)(this.excludeDef,e.excludeDef),(0,core_helpers.LG)(this.keepDef,e.keepDef),(0,core_helpers.LG)(this.mockDef,e.mockDef),(0,core_helpers.LG)(this.replaceDef,e.replaceDef),(0,core_helpers.Nn)(this.configDef,e.configDef),(0,core_helpers.Nn)(this.defProviders,e.defProviders),(0,core_helpers.Nn)(this.defValue,e.defValue),(0,core_helpers.Nn)(this.providerDef,e.providerDef),e}equalsTo(e){for(const o of["beforeCC","keepDef","replaceDef","excludeDef","mockDef"])if(!are_equal_sets(this[o],e[o]))return!1;for(const o of["defValue"])if(!are_equal_maps(this[o],e[o]))return!1;for(const o of["providerDef","defProviders"])if(!are_equal_maps(this[o],e[o],are_equal_providers))return!1;return are_equal_maps(this.configDef,e.configDef,are_equal_config_params)}}function MockBuilder(...e){const[o,t]=e,r=new MockBuilderPerformance(e.length<2?{export:!0}:{dependency:!0}),n=ng_mocks_universe.A.config.get("MockBuilderExtensions");for(const e of n?(0,core_helpers.d4)(n):[]){if((0,helper_extract_property_descriptor.A)(r,e))throw new Error(`MockBuilder.${e} is a base method and cannot be customized, please use a different name.`);(0,core_define_property.A)(r,e,((...o)=>(n.get(e)(r,o),r)))}if(o)for(const e of(0,core_helpers.Bq)(o))r.keep(e,{export:!0,shallow:isStandalone(e)});if(t)for(const e of(0,core_helpers.Bq)(t))r.mock(e,e,{export:!0,exportAll:!0});return r}function mockBuilderExtend(e,o){var t;const r=null!==(t=ng_mocks_universe.A.config.get("MockBuilderExtensions"))&&void 0!==t?t:new Map;o?(r.set(e,o),ng_mocks_universe.A.config.set("MockBuilderExtensions",r)):r.delete(e)}!function(e){e.extend=function(e,o){mockBuilderExtend(e,o)}}(MockBuilder||(MockBuilder={}));const is_debug_node=e=>!!(null==e?void 0:e.nativeElement)||!!(null==e?void 0:e.nativeNode),func_parse_find_args_name=e=>"string"==typeof e?e:"function"==typeof e?e.name:(0,func_is_ng_def.p)(e,"t")?e._desc:Array.isArray(e)?e[0]:e?"":"",is_fixture=e=>!!e&&"object"==typeof e&&void 0!==e.debugElement,findDebugElement=e=>is_fixture(e)?findDebugElement(e.debugElement):e&&e.injector&&e.query?e:void 0,func_parse_find_args=(e,o)=>{var t;let r,n,s=o;return 3===e.length?(r=findDebugElement(e[0]),n=e[1],s=e[2]):1===e.length?(r=findDebugElement(func_get_last_fixture()),[n]=e):e[0]?(r=findDebugElement(e[0]),r?n=e[1]:(r=findDebugElement(func_get_last_fixture()),[n,s]=e)):n=e[1],n=null!==(t=findDebugElement(n))&&void 0!==t?t:n,[r,n,s]};var platform_browser_x=e=>{var o={};return __webpack_require__.d(o,e),o},platform_browser_y=e=>()=>e;const platform_browser_namespaceObject=platform_browser_x({By:()=>__WEBPACK_EXTERNAL_MODULE__angular_platform_browser_bc6fa964__.By}),func_parse_find_term=e=>Array.isArray(e)?platform_browser_namespaceObject.By.css(1===e.length?`[${e[0]}]`:`[${e[0]}="${e[1]}"]`):"string"==typeof e?platform_browser_namespaceObject.By.css(e):platform_browser_namespaceObject.By.directive(getSourceOfMock(e)),defaultNotFoundValue={},mock_helper_find=(...e)=>{const[o,t,r]=func_parse_find_args(e,defaultNotFoundValue),n=is_debug_node(t)?t:null==o?void 0:o.query(func_parse_find_term(t));if(n)return n;if(r!==defaultNotFoundValue)return r;throw new Error(`Cannot find an element via ngMocks.find(${func_parse_find_args_name(t)})`)},detect_text_node=e=>"#text"===e.nativeNode.nodeName,el_def_compare=(e,o)=>!(!e||!o)&&e===o,el_def_get_node=e=>detect_text_node(e)?void 0:e.injector._tNode||e.injector.elDef||void 0,defaultInjector={},core_injector=(e,o=defaultInjector)=>{if(o===defaultInjector)return(0,core_helpers.d5)(e);try{return o.get(e)}catch(e){return}},getVcr=(e,o)=>{if(e!==o&&"#comment"===o.nativeNode.nodeName)return core_injector(core_.ViewContainerRef,o.injector)},getScanViewRefRootNodes=(e,o)=>{const t=getVcr(e,o);if(!t)return[];const r=[];for(let e=0;e{var o;let t,r;for(const n of(null===(o=e.parent)||void 0===o?void 0:o.childNodes)||[])for(const[o,s]of getScanViewRefRootNodes(e,n))s===e.nativeNode&&(void 0===r||o{var o,t,r,n;return(null===(o=e.injector._tNode)||void 0===o?void 0:o.parent)||(null===(t=e.injector.elDef)||void 0===t?void 0:t.parent)||scanViewRef(e)||(null===(r=e.parent)||void 0===r?void 0:r.injector._tNode)||(null===(n=e.parent)||void 0===n?void 0:n.injector.elDef)||void 0},nested_check_children=e=>{var o,t;const r=el_def_get_node(e);if(!r||detect_text_node(e))return[];const n=void 0!==e.childNodes,s=[];for(const t of e.childNodes||(null===(o=e.parent)||void 0===o?void 0:o.childNodes)||[]){const e=el_def_get_parent(t);(n||el_def_compare(r,e))&&(e&&!el_def_compare(r,e)||s.push(t))}if("BODY"===(null===(t=e.parent)||void 0===t?void 0:t.name)){const o=e.parent.childNodes;let t=o.length,r=0;for(let n=o.length-1;n>=0;n-=1){const s=o[n];if("#comment"===s.nativeNode.nodeName)r=n;else if(s.nativeNode===e.nativeNode){t=n+1;break}}for(let e=t;e{var t;if(o)return o;const r=el_def_get_parent(e),n=e.parent?el_def_get_node(e.parent):void 0;if(e.parent&&el_def_compare(r,n))return e.parent;for(const o of(null===(t=e.parent)||void 0===t?void 0:t.childNodes)||[]){const e=el_def_get_node(o);if(el_def_compare(r,e))return o}},nested_check_parent=detectParent,nestedCheck=(e,o,t,r=!1)=>{if(!e)return!1;if(!r&&detect_text_node(e))return!1;if(t(e,nested_check_parent(e,o)))return!0;for(const o of nested_check_children(e))if(nestedCheck(o,e,t,r))return!0;return!1},nested_check=nestedCheck,mock_helper_crawl=(e,o,t=!1)=>{const r=mock_helper_find(func_get_last_fixture(),e,void 0);nested_check(r,void 0,o,t)},isSelector=e=>("string"==typeof e||Array.isArray(e)&&"string"==typeof e[0]||is_fixture(e)||is_debug_node(e),!0),mock_helper_func_parse_find_args=(e,o,t)=>{let r,n,s=t;return 3===e.length?[r,n,s]=e:1===e.length?(r=func_get_last_fixture(),[n]=e):o(e[1])&&isSelector(e[0])?[r,n]=e:(r=func_get_last_fixture(),[n,s]=e),[r,n,s]},detect_attribute_in_selectors=(e,o)=>{for(const t of e){const e=t.match(/\[([^=\]]+)/g);if(e)for(const t of e)if(t===`[${o}`)return!0}return!1},getMeta=e=>{try{return core_reflect_directive_resolve(e)}catch(e){return}},func_parse_provider_tokens_directives=(e,o)=>{if(e)try{const t=(0,func_get_type.A)(o),r=core_injector(t,e.injector);return getMeta(r.constructor)}catch(e){return}},func_get_public_provider_keys=e=>e.injector.elDef?Object.keys(e.injector.elDef.element.publicProviders):[],func_parse_inputs_and_requires_attributes=(e,o)=>{const t=e.injector.elDef.element.publicProviders[o],r=t.provider.value;if(!r)return[[],[],0];const n=func_parse_provider_tokens_directives(e,r),s=t.bindings.map((e=>e.nonMinifiedName||e.name));return[(null==n?void 0:n.inputs)||[],s,t.nodeIndex]},collectSelectors=e=>{const o=[];for(const t of e.providerTokens){const r=func_parse_provider_tokens_directives(e,t);(null==r?void 0:r.selector)&&-1===o.indexOf(r.selector)&&o.push(r.selector)}return o},collectAttributesClassic=e=>{const o=[];for(const t of func_get_public_provider_keys(e)){const[r,n]=func_parse_inputs_and_requires_attributes(e,t);for(const e of r){const{name:t,alias:r}=(0,func_directive_io_parse.A)(e),s=r||t;-1!==n.indexOf(t)&&-1===o.indexOf(s)&&o.push(s)}}return o},collectAttributesIvy=e=>{var o,t;const r=[],n=(null===(o=e.injector._tNode)||void 0===o?void 0:o.attrs)||[];let s=2;for(let o=0;o[collectSelectors(e),[...collectAttributesClassic(e),...collectAttributesIvy(e)]],crawl_by_attribute=e=>o=>{const[t,r]=detect_selectors_from_node(o);return-1!==r.indexOf(e)||!!detect_attribute_in_selectors(t,e)},detectInClassic=(e,o,t)=>{for(const r of func_get_public_provider_keys(e)){const[n,s,c]=func_parse_inputs_and_requires_attributes(e,r);for(const r of n){const{name:n,alias:_}=(0,func_directive_io_parse.A)(r);if(o===(_||n)&&-1!==s.indexOf(n)&&t===e.injector.view.nodes[c].instance[n])return!0}}return!1},detectInIvy=(e,o,t)=>{var r,n,s;const c=(null===(r=e.injector._tNode)||void 0===r?void 0:r.attrs)||[];let _=2;for(let r=0;rt=>!!detectInIvy(t,e,o)||detectInClassic(t,e,o),crawl_by_declaration=e=>{const o=getSourceOfMock(e);return e=>!!e&&-1!==e.providerTokens.indexOf(o)&&void 0!==core_injector(o,e.injector)},crawl_by_id=e=>o=>!!o.references[e],regExp=new RegExp("\\[.*?\\]","g"),detect_tag_name_in_selectors=(e,o)=>{for(const t of e){const e=t.replace(regExp,"").split(",");for(const t of e)if(t.trim()===o)return!0}return!1},crawl_by_tag_name=e=>o=>{const[t]=detect_selectors_from_node(o);return detect_tag_name_in_selectors(t,e)},isCrawlByAttribute=e=>Array.isArray(e)&&1===e.length&&"string"==typeof e[0],isCrawlByAttributeValue=e=>Array.isArray(e)&&2===e.length&&"string"==typeof e[0],isCrawlById=e=>"string"==typeof e&&0===e.indexOf("#")&&e.length>1,isCrawlByTagName=e=>"string"==typeof e&&0!==e.indexOf("#")&&e.length>0,isCrawlByDeclaration=e=>"function"==typeof e,detect_crawler=e=>{if(isCrawlByAttribute(e))return crawl_by_attribute(e[0]);if(isCrawlByAttributeValue(e))return crawl_by_attribute_value(e[0],e[1]);if(isCrawlById(e))return crawl_by_id(e.slice(1));if(isCrawlByTagName(e))return crawl_by_tag_name(e);if(isCrawlByDeclaration(e))return crawl_by_declaration(e);throw new Error("Unknown selector")},func_is_valid_reveal_selector=e=>"string"==typeof e||!(!Array.isArray(e)||"string"!=typeof e[0])||"function"==typeof e,mock_helper_reveal_defaultNotFoundValue={},mock_helper_reveal=(...e)=>{const[o,t,r]=mock_helper_func_parse_find_args(e,func_is_valid_reveal_selector,mock_helper_reveal_defaultNotFoundValue),n=mock_helper_find(func_get_last_fixture(),o,void 0),s=detect_crawler(t);let c;if(mock_helper_crawl(n,(e=>!(e===n||detect_text_node(e)||!s(e)||(c=e,0)))),c)return c;if(r!==mock_helper_reveal_defaultNotFoundValue)return r;throw new Error(`Cannot find a DebugElement via ngMocks.reveal(${func_parse_find_args_name(t)})`)},mock_helper_reveal_all=(...e)=>{const[o,t]=mock_helper_func_parse_find_args(e,func_is_valid_reveal_selector),r=mock_helper_find(func_get_last_fixture(),o,void 0),n=detect_crawler(t),s=[];return mock_helper_crawl(r,(e=>{e!==r&&!detect_text_node(e)&&n(e)&&s.push(e)})),s},isMockControlValueAccessor=e=>!!func_is_mock(e)&&!!e.__ngMocksConfig.isControlValueAccessor;var helper_define_property_descriptor=__webpack_require__(6),helper_extract_methods_from_prototype=__webpack_require__(331);const is_html_element=e=>!!e&&"object"==typeof e&&void 0!==e.innerHTML,preventBubble=["focus","blur","load","unload","change","reset","scroll"],customEvent=(e,o)=>{const t=Object.assign({bubbles:!1,cancelable:!1},o),r=document.createEvent("CustomEvent");return r.initCustomEvent(e,t.bubbles,t.cancelable,null),r},eventCtor="function"==typeof Event?(e,o)=>new CustomEvent(e,o):customEvent,keyMap={alt:{altKey:!0,code:"AltLeft",key:"Alt",location:1,which:18},arrowdown:{code:"ArrowDown",key:"ArrowDown",location:0,which:40},arrowleft:{code:"ArrowLeft",key:"ArrowLeft",location:0,which:37},arrowright:{code:"ArrowRight",key:"ArrowRight",location:0,which:39},arrowup:{code:"ArrowUp",key:"ArrowUp",location:0,which:38},backspace:{code:"Backspace",key:"Backspace",location:0,which:8},control:{code:"ControlLeft",ctrlKey:!0,key:"Control",location:1,which:17},enter:{code:"Enter",key:"Enter",location:0,which:13},esc:{code:"Escape",key:"Escape",location:0,which:27},meta:{code:"MetaLeft",key:"Meta",location:1,metaKey:!0,which:91},shift:{code:"ShiftLeft",key:"Shift",location:1,shiftKey:!0,which:16},space:{code:"Space",key:" ",location:0,which:32},tab:{code:"Tab",key:"Tab",location:0,which:9}};for(let e=1;e<=12;e+=1)keyMap[`f${e}`]={code:`F${e}`,key:`F${e}`,location:0,which:e+111};const getCode=e=>{const o=e.codePointAt(0);return o&&o>=97&&o<=122||o&&o>=65&&o<=90?`Key${e.toUpperCase()}`:o&&o>=48&&o<=57?`Digit${e}`:"Unknown"},applyPayload=(e,o)=>{const t={};for(const e of o?o.split("."):[]){let o=keyMap[e];if(o||1!==e.length||(o={code:getCode(e),key:e}),!o)throw new Error(`Unknown event part ${e}`);(0,mock_helper_stub.A)(t,o)}o&&(0,mock_helper_stub.A)(e,t)},mock_helper_event=(e,o,t)=>{const r=e.indexOf("."),[n,s]=-1===r?[e]:[e.slice(0,Math.max(0,r)),e.slice(r+1)],c=eventCtor(n,Object.assign({bubbles:-1===preventBubble.indexOf(e),cancelable:!0},o));return applyPayload(c,s),t&&(0,mock_helper_stub.A)(c,t),c},mock_helper_trigger_preventBubble=["focus","blur","load","unload","change","reset","scroll"],toEventObj=e=>"string"==typeof e?mock_helper_event(e,{bubbles:-1===mock_helper_trigger_preventBubble.indexOf(e),cancelable:!0}):e,getNativeElement=e=>is_debug_node(e)||is_fixture(e)?e.nativeElement:is_html_element(e)?e:void 0,mock_helper_trigger=(e,o,t)=>{const r=is_html_element(e)?e:mock_helper_find(func_get_last_fixture(),e,void 0),n=getNativeElement(r);if(!n)throw new Error(`Cannot trigger ${"string"==typeof o?o:o.type} event undefined element`);if(n.disabled)return;const s=toEventObj(o);s.target||(0,mock_helper_stub.A)(s,{target:n}),t&&(0,mock_helper_stub.A)(s,t),n.dispatchEvent(s)};var mock_helper_stub_member=__webpack_require__(589);const message=["Cannot find ControlValueAccessor on the element.","If it is a mock input with [formControlName],","you need either to avoid mocking ReactiveFormsModule","or to avoid accessing the control in such a way,","because this tests ReactiveFormsModule instead of own implementation."].join(" "),func_get_vca=e=>{const o=core_form&&core_injector(core_form.NgControl,e.injector),t=null==o?void 0:o.valueAccessor;if(t)return t;const r=core_form&&core_injector(core_form.FormControlDirective,e.injector);if(null==r?void 0:r.form)return r.form;const n=core_form&&core_injector(core_form.NgModel,e.injector);if(n)return n;throw new Error(message)},triggerInput=(e,o)=>{mock_helper_trigger(e,"focus");const t=Object.getOwnPropertyDescriptor(e.nativeElement,"value");(0,mock_helper_stub_member.A)(e.nativeElement,"value",o),mock_helper_trigger(e,"input"),mock_helper_trigger(e,"change"),t&&((0,helper_define_property_descriptor.A)(e.nativeElement,"value",t),e.nativeElement.value=o),mock_helper_trigger(e,"blur")},handleKnown=(e,o)=>core_form&&e instanceof core_form.AbstractControl?(e.setValue(o),!0):core_form&&e instanceof core_form.NgModel?(e.update.emit(o),!0):!!isMockControlValueAccessor(e.instance)&&(e.instance.__simulateChange(o),!0),hasListener=e=>e.listeners.some((e=>"input"===e.name||"change"===e.name)),keys=["onChange","onChangeCallback","onChangeCb","onChangeClb","onChangeFn","_onChange","_onChangeCallback","_onChangeCb","_onChangeClb","_onChangeFn","changeFn","_changeFn","onModelChange","cvaOnChange","cvaOnChangeCallback","cvaOnChangeCb","cvaOnChangeClb","cvaOnChangeFn","_cvaOnChange","_cvaOnChangeCallback","_cvaOnChangeCb","_cvaOnChangeClb","_cvaOnChangeFn"],mock_helper_change=(e,o,t)=>{const r=mock_helper_find(func_get_last_fixture(),e,void 0);if(!r)throw new Error(`Cannot find an element via ngMocks.change(${func_parse_find_args_name(e)})`);const n=func_get_vca(r);if(handleKnown(n,o)||hasListener(r))return void triggerInput(r,o);for(const e of t?[t]:keys)if("function"==typeof n[e])return n.writeValue(o),void n[e](o);const s=(0,helper_extract_methods_from_prototype.A)(n);throw new Error(["Unsupported type of ControlValueAccessor,",`please ensure it has '${t||"onChange"}' method.`,"If it is a 3rd-party library, please provide the correct name of the method in the 'methodName' parameter.","Possible Names: "+s.join(", ")+"."].join(" "))},triggerTouch=e=>{mock_helper_trigger(e,"focus"),mock_helper_trigger(e,"blur")},mock_helper_touch_handleKnown=e=>core_form&&e instanceof core_form.AbstractControl?(e.markAsTouched(),!0):!!isMockControlValueAccessor(e.instance)&&(e.instance.__simulateTouch(),!0),mock_helper_touch_hasListener=e=>e.listeners.some((e=>"focus"===e.name||"blur"===e.name)),mock_helper_touch_keys=["onTouched","onTouchedCallback","onTouchedCb","onTouchedClb","onTouchedFn","_onTouched","_onTouchedCallback","_onTouchedCb","_onTouchedClb","_onTouchedFn","markAsTouched","_markAsTouched","onModelTouched","cvaOnTouch","cvaOnTouchCallback","cvaOnTouchCb","cvaOnTouchClb","cvaOnTouchFn","_cvaOnTouch","_cvaOnTouchCallback","_cvaOnTouchCb","_cvaOnTouchClb","_cvaOnTouchFn"],mock_helper_touch=(e,o)=>{const t=mock_helper_find(func_get_last_fixture(),e,void 0);if(!t)throw new Error(`Cannot find an element via ngMocks.touch(${func_parse_find_args_name(e)})`);const r=func_get_vca(t);if(mock_helper_touch_handleKnown(r)||mock_helper_touch_hasListener(t))return void triggerTouch(t);for(const e of o?[o]:mock_helper_touch_keys)if("function"==typeof r[e])return void r[e]();const n=(0,helper_extract_methods_from_prototype.A)(r);throw new Error(["Unsupported type of ControlValueAccessor,",`please ensure it has '${o||"onTouched"}' method.`,"If it is a 3rd-party library, please provide the correct name of the method in the 'methodName' parameter.","Possible Names: "+n.join(", ")+"."].join(" "))},mock_helper_click=(e,o)=>{mock_helper_trigger(e,"click",o)},mock_helper_find_all=(...e)=>{const[o,t]=func_parse_find_args(e);return is_debug_node(t)?[t]:(null==o?void 0:o.queryAll(func_parse_find_term(t)))||[]},getParentWithInjector=e=>{let o=e;for(;"NullInjector"===(null==o?void 0:o.injector.constructor.name);)o=o.parent;if(o)return o.injector},func_get_from_node_injector=(e,o,t)=>{if(!o.injector||"NullInjector"===o.injector.constructor.name)return;const r=getParentWithInjector(o.parent),n=r?core_injector(t,r):void 0,s=core_injector(t,o.injector);n!==s&&((0,func_is_ng_def.p)(t,"t")&&void 0!==s||void 0!==s&&-1===e.indexOf(s))&&e.push(s)},func_get_from_node_element=e=>{var o;return"#text"===(null===(o=e.nativeNode)||void 0===o?void 0:o.nodeName)&&e.parent?e.parent:e},detectGatherFlag=(e,o,t)=>!!(o&&o.nativeNode&&"#comment"===o.nativeNode.nodeName&&Array.isArray(t)&&t[0]===o.nativeNode)||!Array.isArray(t)&&(o&&t.nodeName?"#comment"===t.nodeName?t===o.nativeNode:"#text"===t.nodeName&&t.parentNode===o.nativeNode:e),isNotObject=e=>!e||"object"!=typeof e,shouldBeScanned=(e,o)=>-1===e.indexOf(o)&&Array.isArray(o),scan=({result:e,el:o,nodes:t,normalize:r,proto:n},s,c=[])=>{c.push(t);let _=s,i=t.length;t.length>1&&t[1]&&"object"==typeof t[1]&&t[1].bindingStartIndex&&(i=t[1].bindingStartIndex);for(let s=0;s{if("object"==typeof e[1]&&e[20]===o)return e;for(let t=21;t{var o;let t=e,r=null===(o=t.nativeNode)||void 0===o?void 0:o.__ngContext__;for(;void 0===r&&t.parent;)t=t.parent,r=t.nativeNode.__ngContext__;if("number"!=typeof r)return r;const n=t.injector._lView;return Array.isArray(n)?detectContextByIndex(n,r):void 0},contextToNodes=e=>Array.isArray(e)?e:null==e?void 0:e.lView,func_get_from_node_ivy=(e,o,t)=>{if(!o||o._debugContext)return;const r=func_get_from_node_element(o);func_get_from_node_scan({el:r,nodes:contextToNodes(detectContext(o))||[],normalize:e=>e,proto:t,result:e},!0)},normalize=e=>{if(!e||"object"!=typeof e)return e;for(const o of["renderElement","renderText","instance"])if(e[o])return e[o];return null},func_get_from_node_standard=(e,o,t)=>{if(!o||!o._debugContext)return;const r=func_get_from_node_element(o);func_get_from_node_scan({el:r,nodes:o._debugContext.view.nodes,normalize,proto:t,result:e},!0)},func_get_from_node=(e,o,t)=>(func_get_from_node_injector(e,o,t),(0,func_is_ng_def.p)(t,"t")||"string"==typeof t||(func_get_from_node_standard(e,o,t),func_get_from_node_ivy(e,o,t)),e),func_is_valid_find_instance_selector=e=>"function"==typeof e||(0,func_is_ng_def.p)(e,"t"),mock_helper_find_instance_defaultNotFoundValue={},mock_helper_find_instance=(...e)=>{const[o,t,r]=mock_helper_func_parse_find_args(e,func_is_valid_find_instance_selector,mock_helper_find_instance_defaultNotFoundValue);if("function"!=typeof t&&!(0,func_is_ng_def.p)(t,"t")&&"string"!=typeof t)throw new Error("Only classes or tokens are accepted");const n=getSourceOfMock(t),s=[],c=func_get_last_fixture();if(c)mock_helper_crawl(mock_helper_find(c,o,void 0),((e,o)=>(func_get_from_node(s,e,n),0===s.length&&o&&"#comment"===o.nativeNode.nodeName&&func_get_from_node(s,o,n),s.length>0)),!0);else try{s.push((0,core_helpers.Ah)(n))}catch(e){if(!e||"object"!=typeof e||void 0===e.ngTempTokenPath)throw e}if(s.length>0)return s[0];if(r!==mock_helper_find_instance_defaultNotFoundValue)return r;throw new Error(`Cannot find an instance via ngMocks.findInstance(${func_parse_find_args_name(t)})`)},mock_helper_find_instances=(...e)=>{const[o,t]=mock_helper_func_parse_find_args(e,func_is_valid_find_instance_selector);if("function"!=typeof t&&!(0,func_is_ng_def.p)(t,"t")&&"string"!=typeof t)throw new Error("Only classes or tokens are accepted");const r=getSourceOfMock(t),n=[],s=[],c=func_get_last_fixture();if(c){const e=mock_helper_find_all(c,o,void 0);for(const o of e)mock_helper_crawl(o,((e,o)=>{-1===s.indexOf(e)&&(func_get_from_node(n,e,r),s.push(e)),o&&"#comment"===o.nativeNode.nodeName&&-1===s.indexOf(o)&&(func_get_from_node(n,o,r),s.push(o))}),!0)}else try{n.push((0,core_helpers.Ah)(r))}catch(e){}return n},handle_array=(e,o)=>e(o.map((o=>e(o,!0))).join("")),format_handler=e=>(o,t=!1)=>{const r=(o,n=!1)=>{if(Array.isArray(o))return handle_array(r,o);if(is_fixture(o))return r(o.debugElement,t);const s=e(r,o,n);return void 0!==s?s:is_debug_node(o)&&"#comment"===o.nativeNode.nodeName?r(nested_check_children(o),!0):is_debug_node(o)?r(o.nativeNode,n):""};return Array.isArray(o)?o.map((e=>r(e,t))):r(o,t)},handle_text=e=>{var o,t;return null!==(t=null!==(o=e.nodeValue)&&void 0!==o?o:e.textContent)&&void 0!==t?t:e.wholeText},is_text=e=>!!e&&"object"==typeof e&&"#text"===e.nodeName,normalizeValue=e=>e?e.replace(new RegExp("\\s+","mg")," ").replace(new RegExp("\x3c!--(.|\\n|\\r)*?--\x3e|\x3c!--(.|\\n|\\r)*","mg"),"").replace(new RegExp("\\s+","mg")," ").replace(new RegExp(">\\s+<","mg"),"><").replace(new RegExp('"\\s+>',"mg"),'">'):"",normalizeText=e=>e.replace(new RegExp("&","mg"),"&").replace(new RegExp('"',"mg"),""").replace(new RegExp("<","mg"),"<").replace(new RegExp(">","mg"),">").replace(new RegExp("'","mg"),"'"),getElementValue=(e,o)=>o?e.outerHTML:e.innerHTML,handlePrimitives=(e,o,t)=>{if("string"==typeof o||void 0===o){const e=normalizeValue(o);return t?e:e.trim()}return is_html_element(o)?e(getElementValue(o,t)):is_text(o)?handlePrimitives(e,normalizeText(handle_text(o)),t):void 0},mock_helper_format_html=format_handler(handlePrimitives),mock_helper_format_text_normalizeValue=e=>e?e.replace(new RegExp("\\s+","mg")," "):"",mock_helper_format_text_getElementValue=(e,o)=>{var t;const r=null!==(t=e.textContent)&&void 0!==t?t:"";return o?r:r.trim()},mock_helper_format_text_handlePrimitives=(e,o,t)=>{if("string"==typeof o||void 0===o){const e=mock_helper_format_text_normalizeValue(o);return t?e:e.trim()}return is_html_element(o)?e(mock_helper_format_text_getElementValue(o,t)):is_text(o)?mock_helper_format_text_handlePrimitives(e,handle_text(o),t):void 0},mock_helper_format_text=format_handler(mock_helper_format_text_handlePrimitives),calls=[],mock_helper_auto_spy=e=>{"reset"===e?calls.pop():calls.push(e);const o=calls[calls.length-1];return"jasmine"===o?(0,helper_mock_service.O)((e=>jasmine.createSpy(e))):"jest"===o?(0,helper_mock_service.O)((e=>jest.fn().mockName(e))):o&&"default"!==o&&"reset"!==o?(0,helper_mock_service.O)(o):(0,helper_mock_service.O)()},mock_helper_console=(e,o)=>(...t)=>{const r=[];beforeEach((()=>{for(const o of e)-1===t.indexOf(o)&&t.push(o);for(const e of t)(0,core_define_property.A)(console,`__ngMocksBackup_${e}`,console[`__ngMocksBackup_${e}`]||[]),console[`__ngMocksBackup_${e}`].push(console[e]),r.push(e),console[e]=o(e)})),afterEach((()=>{for(const e of r)console[e]=console[`__ngMocksBackup_${e}`].pop();r.splice(0,r.length)}))},factory=e=>helper_mock_service.A.mockFunction(`console.${e}`),mock_helper_console_ignore=mock_helper_console(["log"],factory),mock_helper_console_throw_factory=e=>(...o)=>{const t=new Error(o.join(" "));throw(0,core_define_property.A)(t,"ngMocksConsoleCatch",e),t},mock_helper_console_throw=mock_helper_console(["warn","error"],mock_helper_console_throw_factory),mock_helper_default_config=(e,o)=>{const t=ng_mocks_universe.A.getConfigMock();for(const r of(0,core_helpers.Bq)(e))o?t.set(r,o):t.delete(r)},mock_helper_default_mock=(e,o)=>{const t=ng_mocks_universe.A.getOverrides();for(const r of(0,core_helpers.Bq)(e))if(o){const e=t.has(r)?t.get(r):new Set;e.add(o),t.set(r,e)}else t.delete(r)},hooks=ng_mocks_universe.A.global.get("faster-hooks")||{after:[],before:[]};ng_mocks_universe.A.global.set("faster-hooks",hooks);const configureTestingModule=(e,o)=>t=>{if(testing_.TestBed.ngMocksFasterLock)return e.call(o,t);ng_mocks_universe.A.global.set("bullet:customized",!0);let r=e;for(const e of hooks.before)r=e(r,o);try{return(0,core_define_property.A)(testing_.TestBed,"ngMocksFasterLock",!0),r.call(o,t)}finally{(0,core_define_property.A)(testing_.TestBed,"ngMocksFasterLock",void 0)}},resetTestingModule=(e,o)=>()=>{if(testing_.TestBed.ngMocksFasterLock)return e.call(o);if(ng_mocks_universe.A.global.has("bullet"))return ng_mocks_universe.A.global.has("bullet:customized")&&ng_mocks_universe.A.global.set("bullet:reset",!0),o;ng_mocks_universe.A.global.delete("bullet:customized"),ng_mocks_universe.A.global.delete("bullet:reset");let t=e;for(const e of hooks.after)t=e(t,o);try{return(0,core_define_property.A)(testing_.TestBed,"ngMocksFasterLock",!0),t.call(o)}finally{(0,core_define_property.A)(testing_.TestBed,"ngMocksFasterLock",void 0)}},mock_helper_faster_install=()=>{testing_.TestBed.ngMocksFasterInstalled||(testing_.TestBed.configureTestingModule=configureTestingModule(testing_.TestBed.configureTestingModule,testing_.TestBed),testing_.TestBed.resetTestingModule=resetTestingModule(testing_.TestBed.resetTestingModule,testing_.TestBed),(0,core_define_property.A)(testing_.TestBed,"ngMocksFasterInstalled",!0));const e=(0,testing_.getTestBed)();return e.ngMocksFasterInstalled||(e.configureTestingModule=configureTestingModule(e.configureTestingModule,e),e.resetTestingModule=resetTestingModule(e.resetTestingModule,e),(0,core_define_property.A)(e,"ngMocksFasterInstalled",!0)),hooks},mock_helper_flush_test_bed=()=>{const e=(0,testing_.getTestBed)();e._instantiated=!1,e._moduleFactory=void 0,e._testModuleRef=null},resetFixtures=e=>{const o=(0,testing_.getTestBed)()._activeFixtures||[];let t=0;for(let r=o.length-1;r>=0;r-=1)o[r].ngMocksStackId&&o[r].ngMocksStackId!==e?t+=1:(o[r].ngMocksStackId=void 0,o[r].destroy(),o.splice(r,1));0===t&&mock_helper_flush_test_bed()},idAdd=e=>{var o;const t=null!==(o=ng_mocks_universe.A.global.get("bullet:stack"))&&void 0!==o?o:[];t.push(e),ng_mocks_universe.A.global.set("bullet:stack",t),ng_mocks_universe.A.global.set("bullet:stack:id",e)},idRemove=e=>{const o=ng_mocks_universe.A.global.get("bullet:stack");o.splice(o.indexOf(e),1),o.length>0?ng_mocks_universe.A.global.set("bullet:stack:id",o[o.length-1]):ng_mocks_universe.A.global.delete("bullet:stack:id"),resetFixtures(e)},mock_helper_faster=()=>{mock_helper_faster_install();const e={},o={};beforeAll((()=>{ng_mocks_universe.A.global.has("bullet:customized")&&testing_.TestBed.resetTestingModule(),ng_mocks_universe.A.global.set("bullet",!0),idAdd(e)})),beforeEach((()=>{idAdd(o)})),afterEach((()=>{idRemove(o)})),afterAll((()=>{idRemove(e),ng_mocks_universe.A.global.delete("bullet"),ng_mocks_universe.A.global.has("bullet:reset")&&testing_.TestBed.resetTestingModule()}))},mock_helper_get_defaultNotFoundValue={},parseArgs=e=>({el:e[0],notFoundValue:3===e.length?e[2]:mock_helper_get_defaultNotFoundValue,sel:e[1]}),mock_helper_get=(...e)=>{if(1===e.length)try{return testing_.TestBed.inject?testing_.TestBed.inject(e[0]):testing_.TestBed.get(e[0])}catch(o){if(!o||"object"!=typeof o||void 0===o.ngTempTokenPath)throw o;throw new Error(`Cannot find an instance via ngMocks.get(${func_parse_find_args_name(e[0])})`)}const{el:o,sel:t,notFoundValue:r}=parseArgs(e),n=mock_helper_find(func_get_last_fixture(),o,void 0),s=getSourceOfMock(t);if(n){const e=func_get_from_node([],n,s);if(e.length>0)return e[0]}if(n){const e=nested_check_parent(n,void 0);if(e&&"#comment"===e.nativeNode.nodeName){const o=func_get_from_node([],e,s);if(o.length>0)return o[0]}}if(r!==mock_helper_get_defaultNotFoundValue)return r;throw new Error(`Cannot find ${(0,func_get_name.A)(t)} instance via ngMocks.get`)},iterator=(e,o,t=new Set)=>{const r=(0,collect_declarations.A)(e);for(const e of r.decorators)for(const n of core_config.A.dependencies)if(r[e][n])for(const s of(0,core_helpers.Bq)(r[e][n])){const e=(0,func_get_type.A)(s);e&&!t.has(e)&&(t.add(e),o(e),iterator(e,o,t))}},func_iterate_declaration=iterator,func_global_prepare=()=>{var e;ng_mocks_universe.A.cacheDeclarations.clear(),null===(e=ng_mocks_universe.A.config.get("ngMocksDepsSkip"))||void 0===e||e.clear()},action=e=>{ng_mocks_universe.A.getDefaults().set(e,["exclude"])},mock_helper_global_exclude=(e,o=!1)=>{func_global_prepare(),action(e),o&&func_iterate_declaration(e,action)},mock_helper_global_keep_action=e=>{ng_mocks_universe.A.getDefaults().set(e,["keep"])},mock_helper_global_keep=(e,o=!1)=>{func_global_prepare(),mock_helper_global_keep_action(e),o&&func_iterate_declaration(e,mock_helper_global_keep_action)},mock_helper_global_mock_action=e=>{ng_mocks_universe.A.getDefaults().set(e,["mock"])},mock_helper_global_mock=(e,o=!1)=>{func_global_prepare(),mock_helper_global_mock_action(e),o&&func_iterate_declaration(e,mock_helper_global_mock_action)},mock_helper_global_replace=(e,o)=>{let t=!0;if(((0,func_is_ng_def.p)(e,"m")&&(0,func_is_ng_def.p)(o,"m")||(0,func_is_ng_def.p)(e,"c")&&(0,func_is_ng_def.p)(o,"c")||(0,func_is_ng_def.p)(e,"d")&&(0,func_is_ng_def.p)(o,"d")||(0,func_is_ng_def.p)(e,"p")&&(0,func_is_ng_def.p)(o,"p"))&&(t=!1),t)throw new Error("Cannot replace the declaration, both have to be a Module, a Component, a Directive or a Pipe");func_global_prepare(),ng_mocks_universe.A.getDefaults().set(e,["replace",o])},mock_helper_global_wipe_action=e=>{ng_mocks_universe.A.getDefaults().delete(e),mock_helper_default_mock(e)},mock_helper_global_wipe=(e,o=!1)=>{func_global_prepare(),mock_helper_global_wipe_action(e),o&&func_iterate_declaration(e,mock_helper_global_wipe_action)},mock_helper_guts_skipDef=(e,o,t)=>!!o.has(e)||(o.add(e),t.has(e)),createMetaHandler=(e,o,t,r,n)=>{const s=e.get(o)||o;(0,func_is_ng_def.p)(s,"m")?t.push(s):(0,func_is_ng_def.p)(s,"c")||(0,func_is_ng_def.p)(s,"d")?r.push(s):(0,func_is_ng_def.p)(s,"p")?(r.push(s),n.push(s)):(0,func_is_ng_injection_token.S)(s)||n.push(s)},createMeta=({keep:e,skip:o,optional:t,exclude:r,imports:n,declarations:s,providers:c})=>{for(const _ of e)o.has(_)||r.has(_)||t.has(_)||createMetaHandler(t,_,n,s,c);return{declarations:s,imports:n,providers:c}},typeMap=[["m","module"],["c","component"],["d","directive"],["p","pipe"]],mock_helper_guts_getType=(e,o)=>{if((0,func_is_ng_module_def_with_providers.h)(e))return"module-with-providers";for(const[t,r]of typeMap)if((0,func_is_ng_def.p)(e,t))return"m"===t&&o.has(e)?`${r}-keep`:r;return""},handleModuleWithProviders=(e,o)=>{e.skip.has(o.ngModule)||(e.skip.add(o.ngModule),e.exclude.has(o.ngModule)||e.imports.push(e.keep.has(o.ngModule)?o:MockModule(o)))},handleDeclaration=(e,o,t,r)=>{mock_helper_guts_skipDef(o,e.skip,e.exclude)||r.push(e.keep.has(o)?o:t(o))},handleDestructuring=(e,o,t)=>{if(mock_helper_guts_skipDef(o,e.skip,e.exclude))return;const r=core_reflect_module_resolve(o);for(const o of(0,core_helpers.Bq)([r.declarations,r.imports]))t(e,o);for(const o of r.providers?(0,core_helpers.Bq)(r.providers):[])resolveProvider(e,o)},resolveProvider=({skip:e,keep:o,providers:t,exclude:r},n)=>{const s=(0,func_get_type.A)(n);if(e.add(s),r.has(s))return;const c=o.has(s)?n:(0,mock_provider.A)(n);c&&t.push(c)},resolveMap={component:MockComponent,directive:MockDirective,pipe:MockPipe},resolveHandler=(e,o,t,r)=>{"module-with-providers"===o?handleModuleWithProviders(e,t):"module-keep"===o||"module"===o&&r?handleDeclaration(e,t,MockModule,e.imports):"module"===o?handleDestructuring(e,t,resolve):resolveMap[o]?handleDeclaration(e,t,resolveMap[o],e.declarations):resolveProvider(e,t)},resolve=(e,o,t=!0)=>{if(!o)return;const r=mock_helper_guts_getType(o,e.keep);let n;if("module-with-providers"!==r){const t=e.optional.get(o);t&&t!==o&&(n=t,e.keep.add(n))}n||(n=o),resolveHandler(e,r,n,t)},generateDataWithUniverse=(e,o,t,r)=>{for(const n of(0,core_helpers.d4)(ng_mocks_universe.A.getDefaults())){const s=ng_mocks_universe.A.getBuildDeclaration(n);e.has(n)||o.has(n)||t.has(n)||(r.set(n,s),null===s?t.add(n):void 0===s?o.add(n):n===s&&e.add(n))}},generateData=(e,o,t)=>{const r=new Set((0,core_helpers.Bq)(e||[])),n=new Set((0,core_helpers.Bq)(o||[])),s=new Set((0,core_helpers.Bq)(t||[])),c=new Map;return generateDataWithUniverse(r,n,s,c),{declarations:[],exclude:s,imports:[],keep:r,mock:n,optional:c,providers:[],skip:new Set}},mock_helper_guts=(e,o=null,t=null)=>{const r=generateData(e,o,t),n=new Map;ng_mocks_universe.A.config.set("ngMocksDepsResolution",n);for(const e of(0,core_helpers.LG)(r.keep))n.set(e,"keep");for(const e of(0,core_helpers.LG)(r.exclude))n.set(e,"exclude");ng_mocks_universe.A.config.set("mockNgDefResolver",new core_def_stack);for(const e of(0,core_helpers.LG)(r.mock))n.set(e,"mock"),r.optional.has(e)||resolve(r,e,!1);const s=createMeta(r);return ng_mocks_universe.A.config.delete("mockNgDefResolver"),ng_mocks_universe.A.config.delete("ngMocksDepsResolution"),s},mock_helper_attributes_defaultNotFoundValue={},mock_helper_attributes_parseArgs=e=>[e[0],e[1],3===e.length?e[2]:mock_helper_attributes_defaultNotFoundValue],attrMatches=(e,o)=>{const{name:t,alias:r=""}=(0,func_directive_io_parse.A)(e);if(!r&&t===o||r&&r===o)return t},detectAttribute=(e,o,t)=>{for(const r of(null==e?void 0:e.providerTokens)||[]){const n=func_parse_provider_tokens_directives(e,r);if(n)for(const s of n[o]||[]){const o=attrMatches(s,t);if(o)return mock_helper_get(e,r)[o]}}throw new Error("Not found")},mock_helper_attributes=(e,o,...t)=>{const[r,n,s]=mock_helper_attributes_parseArgs(t);try{return detectAttribute(mock_helper_find(func_get_last_fixture(),r,void 0),o,n)}catch(e){}if(s!==mock_helper_attributes_defaultNotFoundValue)return s;throw new Error(`Cannot find ${n} ${e} via ngMocks.${e}`)},mock_helper_input=(...e)=>mock_helper_attributes("input","inputs",...e),mock_helper_output=(...e)=>mock_helper_attributes("output","outputs",...e),mock_helper_reset=()=>{ng_mocks_universe.A.builtDeclarations=new Map,ng_mocks_universe.A.builtProviders=new Map,ng_mocks_universe.A.cacheDeclarations=new Map,ng_mocks_universe.A.cacheProviders=new Map,ng_mocks_universe.A.config=new Map,ng_mocks_universe.A.configInstance=new Map,ng_mocks_universe.A.flags=new Set(core_config.A.flags),ng_mocks_universe.A.touches=new Set},getValVcr=e=>{const o=[];for(const t of e.__ngMocksConfig.queryScanKeys||[]){const r=e[t],n=e[`__ngMocksVcr_${t}`],s=r instanceof core_.QueryList?r.toArray():[r],c=n instanceof core_.QueryList?n.toArray():[n];for(let e=0;e!!e.__template&&!!e.__vcr&&o(e.__template)&&t(e.__vcr,e.__template),isRightTemplate=(e,o,t)=>!!e&&o instanceof core_.TemplateRef&&t(o),findDeep=(e,o,t)=>{if(!func_is_mock(e))throw new Error("Only instances of mock declarations are accepted");if(handleDirective(e,o,t))return!0;for(const[r,n]of getValVcr(e)){if(func_is_mock(r)&&findDeep(r,o,t))return!0;if(isRightTemplate(n,r,o))return t(n,r)}return!1},func_find_deep=findDeep,func_parse_template=e=>{if(e instanceof core_.TemplateRef)return e;if(func_is_mock(e)&&e.__template)return e.__template;const o=(null==e?void 0:e.nativeNode)&&e.injector;if(o){const e=core_injector(core_.TemplateRef,o);if(e)return e}const t=new Error("Unknown template has been passed, only TemplateRef or a mock structural directive are supported");throw t.param=e,t},mock_helper_hide=(e,o)=>{const t=o?func_parse_template(o):void 0;let r=!1;if(func_find_deep(e,(e=>!t||e.elementRef.nativeElement===t.elementRef.nativeElement),(e=>(e.clear(),r=!0,!1))),!r)throw new Error("Cannot find path to the TemplateRef")},mock_helper_render=(e,o,t,r)=>{const n=func_parse_template(o);if(!func_find_deep(e,(e=>e.elementRef.nativeElement===n.elementRef.nativeElement),((e,o)=>{const n=Object.assign(Object.assign({},r),{$implicit:t});return e.clear(),e.createEmbeddedView(o,n).detectChanges(),!0})))throw new Error("Cannot find path to the TemplateRef")},template_ref_detect_crawler=e=>{if("string"==typeof e)return crawl_by_id(e);if(Array.isArray(e)&&1===e.length&&"string"==typeof e[0])return crawl_by_attribute(e[0]);if(Array.isArray(e)&&2===e.length&&"string"==typeof e[0])return crawl_by_attribute_value(e[0],e[1]);if("function"==typeof e)return crawl_by_declaration(e);throw new Error("Unknown selector")},detect_template_ref=(e,o,t=0)=>r=>{try{const t=!detect_text_node(r)&&o(r)?core_injector(core_.TemplateRef,r.injector):void 0;t&&e.push(t)}catch(e){}return!!t&&e.length===t},func_is_valid_template_ref_selector=e=>"string"==typeof e||!(!Array.isArray(e)||"string"!=typeof e[0])||"function"==typeof e,mock_helper_find_template_ref_defaultNotFoundValue={},mock_helper_find_template_ref=(...e)=>{const[o,t,r]=mock_helper_func_parse_find_args(e,func_is_valid_template_ref_selector,mock_helper_find_template_ref_defaultNotFoundValue),n=[],s=template_ref_detect_crawler(t);if(nested_check(mock_helper_find(func_get_last_fixture(),o,void 0),void 0,detect_template_ref(n,s,1)),n.length>0)return n[0];if(r!==mock_helper_find_template_ref_defaultNotFoundValue)return r;throw new Error(`Cannot find a TemplateRef via ngMocks.findTemplateRef(${func_parse_find_args_name(t)})`)},mock_helper_find_template_refs=(...e)=>{const[o,t]=mock_helper_func_parse_find_args(e,func_is_valid_template_ref_selector),r=[],n=template_ref_detect_crawler(t);return nested_check(mock_helper_find(func_get_last_fixture(),o,void 0),void 0,detect_template_ref(r,n)),r},flagNames=["onMockBuilderMissingDependency","onMockInstanceRestoreNeed","onTestBedFlushNeed"],mock_helper_object={autoSpy:mock_helper_auto_spy,change:mock_helper_change,click:mock_helper_click,config:e=>{const o=ng_mocks_universe.A.global.get("flags");for(const t of flagNames)null===e[t]?o[t]=core_config.A[t]:void 0!==e[t]&&(o[t]=e[t]);null===e.mockRenderCacheSize?ng_mocks_universe.A.global.delete("mockRenderCacheSize"):void 0!==e.mockRenderCacheSize&&ng_mocks_universe.A.global.set("mockRenderCacheSize",e.mockRenderCacheSize)},crawl:mock_helper_crawl,defaultConfig:mock_helper_default_config,defaultMock:mock_helper_default_mock,event:mock_helper_event,faster:mock_helper_faster,find:mock_helper_find,findAll:mock_helper_find_all,findInstance:mock_helper_find_instance,findInstances:mock_helper_find_instances,findTemplateRef:mock_helper_find_template_ref,findTemplateRefs:mock_helper_find_template_refs,flushTestBed:mock_helper_flush_test_bed,formatHtml:mock_helper_format_html,formatText:mock_helper_format_text,get:mock_helper_get,globalExclude:mock_helper_global_exclude,globalKeep:mock_helper_global_keep,globalMock:mock_helper_global_mock,globalReplace:mock_helper_global_replace,globalWipe:mock_helper_global_wipe,guts:mock_helper_guts,hide:mock_helper_hide,ignoreOnConsole:mock_helper_console_ignore,input:mock_helper_input,output:mock_helper_output,render:mock_helper_render,reset:mock_helper_reset,reveal:mock_helper_reveal,revealAll:mock_helper_reveal_all,stub:mock_helper_stub.A,stubMember:mock_helper_stub_member.A,throwOnConsole:mock_helper_console_throw,touch:mock_helper_touch,trigger:mock_helper_trigger},ngMocks=mock_helper_object;var mock_service=__webpack_require__(839);const defaultValue={};function MockProviders(...e){return e.map((e=>MockProvider(e,defaultValue)))}function MockProvider(e,o=defaultValue,t,r={}){func_import_exists(e,"MockProvider");const{deps:n,multi:s}="boolean"==typeof r?{deps:void 0,multi:r}:Array.isArray(r)?{deps:r,multi:void 0}:r;return t?{provide:e,[t]:o,deps:n,multi:s}:(0,helper_use_factory.A)(e,(()=>(0,mock_service.K)(e)),(e=>o===defaultValue?e:e?(0,mock_helper_stub.A)(e,o):o))}const applyOverride=(e,o)=>{(0,func_is_ng_def.p)(e,"c")?testing_.TestBed.overrideComponent(e,o):(0,func_is_ng_def.p)(e,"d")?testing_.TestBed.overrideDirective(e,o):(0,func_is_ng_def.p)(e,"m")&&testing_.TestBed.overrideModule(e,o),((0,func_is_ng_def.p)(e,"t")||(0,func_is_ng_def.p)(e,"i"))&&testing_.TestBed.overrideProvider(e,o)},ng_mocks_global_overrides_applyOverrides=e=>{for(const[o,[t,r]]of(0,core_helpers.Nn)(e))testing_.TestBed.ngMocksOverrides.set(o,Object.assign(Object.assign({},r),{override:t})),applyOverride(o,t)},applyNgMocksOverrides=e=>{var o;if(null===(o=e.ngMocksOverrides)||void 0===o?void 0:o.size){ngMocks.flushTestBed();for(const[o,t]of(0,core_helpers.Nn)(e.ngMocksOverrides))applyOverride(o,t)}e.ngMocksOverrides=void 0},initTestBed=()=>{testing_.TestBed.ngMocksSelectors||(0,core_define_property.A)(testing_.TestBed,"ngMocksSelectors",new Map),testing_.TestBed.ngMocksOverrides||(0,core_define_property.A)(testing_.TestBed,"ngMocksOverrides",new Map)},generateTouches=(e,o)=>{for(const t of core_config.A.dependencies)for(const r of e[t]?(0,core_helpers.Bq)(e[t]):[]){const e=(0,func_get_type.A)(r);if((0,func_is_ng_module_def_with_providers.h)(r)&&generateTouches(r,o),!o.has(e)&&(o.add(e),"function"==typeof e)){if(!Object.prototype.hasOwnProperty.call(e,"__ngMocksTouches")){const o=new Set,t=core_reflect_meta(e);(0,core_define_property.A)(e,"__ngMocksTouches",o,!1),t&&generateTouches(t,o)}(0,core_helpers.LG)(e.__ngMocksTouches,o)}}},defineTouches=(e,o,t)=>{var r;let n=t;return!n&&ng_mocks_universe.A.getDefaults().size>0&&(n=func_extract_tokens(e._providers||(null===(r=e._compiler)||void 0===r?void 0:r.providers)).touches,n||(n=new Set,o.providers=o.providers||[],o.providers.push({provide:core_tokens.Em,useValue:n})),generateTouches(o,n)),n},applyPlatformOverrideDef=e=>{const o=(0,func_get_type.A)(e);if(testing_.TestBed.ngMocksOverrides.has(o))return;const t=core_reflect_module_resolve(o),r=get_override_def(t);r&&(testing_.TestBed.ngMocksOverrides.set(o,{set:t}),testing_.TestBed.overrideModule(o,{set:r}))},applyPlatformOverridesBasedOnProvidedIn=(e,o)=>{const t=core_reflect_provided_in(e);t&&("string"==typeof t||o.has(t))&&(testing_.TestBed.ngMocksOverrides.set(e,{}),testing_.TestBed.overrideProvider(e,MockProvider(e)))},applyPlatformOverridesBasedOnDefaults=e=>{for(const[o,[t]]of(0,core_helpers.Nn)(ng_mocks_universe.A.getDefaults()))"mock"===t&&((0,func_is_ng_def.p)(o,"i")||(0,func_is_ng_def.p)(o,"t"))&&(e.has(o)||testing_.TestBed.ngMocksOverrides.has(o)||applyPlatformOverridesBasedOnProvidedIn(o,e))},applyPlatformOverrides=(e,o)=>{if(testing_.TestBed.ngMocksOverrides){const t=ng_mocks_universe.A.touches;ng_mocks_universe.A.touches=o;for(const o of(0,core_helpers.Bq)(e.ngModule||[]))applyPlatformOverrideDef(o);applyPlatformOverridesBasedOnDefaults(o),ng_mocks_universe.A.touches=t}},ng_mocks_global_overrides_configureTestingModule=(e,o)=>t=>{var r,n;initTestBed();const s=!("object"!=typeof t||!t||t.providers&&-1!==t.providers.indexOf(MockBuilder));let c=0;const _=[];for(const e of s?["imports","declarations"]:[])for(const o of(0,core_helpers.Bq)(t[e]))o&&(_.push([(0,func_is_ng_module_def_with_providers.h)(o)?{ngModule:getSourceOfMock(o.ngModule),providers:o.providers}:getSourceOfMock(o),(0,func_is_ng_module_def_with_providers.h)(o)?o.ngModule:o,isMockNgDef((0,func_get_type.A)(o))]),c|=_[_.length-1][2]?2:1);let i=3===c?void 0:t;if(!i){let e=MockBuilder(core_tokens.gG);for(const[o,t,n]of _){const s=null===(r=t.prototype.__ngMocksConfig)||void 0===r?void 0:r.transform,c={export:!(0,func_is_ng_def.p)(o,"m"),exportAll:!1,onRoot:!0};e=n&&s?e.mock(o,s,c):n?e.mock(o,c):e.keep(o,c)}i=e.build(),i=Object.assign(Object.assign(Object.assign({},t),i),{providers:[...null!==(n=t.providers)&&void 0!==n?n:[],...i.providers]})}const a=(0,testing_.getTestBed)(),l=func_extract_tokens(i.providers),{mocks:p,overrides:u}=l,d=defineTouches(a,i,l.touches);return p&&ngMocks.flushTestBed(),u&&ng_mocks_global_overrides_applyOverrides(u),!d||a._instantiated||a._testModuleRef||applyPlatformOverrides(a,d),e.call(o,i)},ng_mocks_global_overrides_resetTestingModule=(e,o)=>()=>(ng_mocks_universe.A.global.delete("builder:config"),ng_mocks_universe.A.global.delete("builder:module"),testing_.TestBed.ngMocksSelectors=void 0,applyNgMocksOverrides(testing_.TestBed),e.call(o)),patchVcrInstance=e=>{if(!core_.ViewContainerRef.ngMocksOverridesPatched&&((0,core_define_property.A)(core_.ViewContainerRef,"ngMocksOverridesPatched",!0),e.createComponent)){const o=e.createComponent,t=(0,helper_create_clone.A)(o,void 0,void 0,(function(e,...t){var r;const n=core_injector(core_tokens.en,this.injector);return o.apply(this,[null!==(r=null==n?void 0:n.get(e))&&void 0!==r?r:e,...t])}));(0,core_define_property.A)(e.constructor.prototype,"createComponent",t,!0),(0,core_define_property.A)(e,"createComponent",t,!0)}},createComponent=(e,o)=>t=>{const r=e.call(o,t);try{const e=r.debugElement.injector.get(core_.ViewContainerRef);patchVcrInstance(e)}catch(e){}return r},viewContainerInstall=()=>{const e=core_.ViewContainerRef;if(!e.ngMocksOverridesInstalled){const o=e.__NG_ELEMENT_ID__;o&&(0,core_define_property.A)(e,"__NG_ELEMENT_ID__",(0,helper_create_clone.A)(o,void 0,void 0,((...e)=>{const t=o.apply(o,e);return patchVcrInstance(t),t})),!0),(0,core_define_property.A)(testing_.TestBed,"createComponent",createComponent(testing_.TestBed.createComponent,testing_.TestBed)),(0,core_define_property.A)(core_.ViewContainerRef,"ngMocksOverridesInstalled",!0)}},installInjector=e=>{if(e.constructor.prototype.__ngMocksInjector||!e.constructor.prototype.get)return e;(0,core_define_property.A)(e.constructor.prototype,"__ngMocksInjector",!0);const o=e.constructor.prototype.get;return e.constructor.prototype.get=(0,helper_create_clone.A)(o,void 0,void 0,(function(e,...t){const r=o.call(this,e,...t);return r&&"object"==typeof r&&"function"==typeof r.constructor&&"string"==typeof r.constructor.name&&"Injector"===r.constructor.name.slice(-8)&&installInjector(r),r})),e},install=()=>{if(!testing_.TestBed.ngMocksOverridesInstalled){const e=mock_helper_faster_install();viewContainerInstall(),-1===e.before.indexOf(ng_mocks_global_overrides_configureTestingModule)&&e.before.push(ng_mocks_global_overrides_configureTestingModule),-1===e.after.indexOf(ng_mocks_global_overrides_resetTestingModule)&&e.after.push(ng_mocks_global_overrides_resetTestingModule),(0,core_define_property.A)(testing_.TestBed,"ngMocksOverridesInstalled",!0);const o=core_.Injector.create;core_.Injector.create=(0,helper_create_clone.A)(o,void 0,void 0,((...e)=>installInjector(o.apply(core_.Injector,e))));try{core_.Injector.create({length:0,providers:[]})}catch(e){}}};function isMockedNgDefOf(e,o,t){return"function"==typeof e&&e.mockOf===o&&(!t||(0,func_is_ng_def.p)(e,t))}install();const getMock=(e,o,t)=>{if(t&&!t.has(o))throw new Error(`There is no mock for ${(0,func_get_name.A)(o)}`);let r=t?t.get(o):void 0;return r===o&&(r=void 0),r||o===e?!r&&ng_mocks_universe.A.cacheDeclarations.has(o)&&(r=ng_mocks_universe.A.cacheDeclarations.get(o)):r=e,r};function getMockedNgDefOf(e,o){var t;const r=null!==(t=e.mockOf)&&void 0!==t?t:e,n=core_injector(core_tokens.en),s=getMock(e,r,n);if(s&&!o)return s;if(s&&o&&isMockedNgDefOf(s,r,o))return s;throw new Error(`There is no mock for ${(0,func_get_name.A)(r)}`)}function isMockOf(e,o,t){return func_is_mock(e)&&e.constructor===o&&(t?(0,func_is_ng_def.p)(e.constructor,t):(0,func_is_ng_def.p)(e.constructor))}const isMockValidator=e=>!!func_is_mock(e)&&!!e.__ngMocksConfig.isValidator,mock_instance_forgot_reset=e=>{const o=[];for(;e.length>0;){const[t,r]=e.pop()||[];r===ng_mocks_universe.A.configInstance.get(t)&&o.push("function"==typeof t?(0,func_get_name.A)(t):t)}if(o.length>0){const e=ng_mocks_universe.A.global.get("flags"),t=[`MockInstance: side effects have been detected (${o.join(", ")}).`,"Forgot to add MockInstance.scope() or to call MockInstance.restore()?"].join(" ");if("warn"===e.onMockInstanceRestoreNeed)console.warn(t);else if("throw"===e.onMockInstanceRestoreNeed)throw new Error(t)}};let currentStack;ng_mocks_stack.subscribePush((e=>{currentStack=e})),ng_mocks_stack.subscribePop(((e,o)=>{for(const o of e.mockInstance||[])if(ng_mocks_universe.A.configInstance.has(o)){const e=ng_mocks_universe.A.configInstance.get(o);e.overloads.pop(),ng_mocks_universe.A.configInstance.set(o,Object.assign({},e))}currentStack=o[o.length-1]}));const parseMockInstanceArgs=e=>{const o={};return"string"==typeof e[0]?(o.key=e[0],o.value=e[1],o.accessor=e[2]):(o.value=e[0],o.value&&"object"==typeof o.value&&(o.value=o.value.init)),o},checkReset=[];let checkCollect=!1;"undefined"!=typeof beforeEach&&(beforeEach((()=>checkCollect=!0)),beforeEach((()=>mock_instance_forgot_reset(checkReset))),afterEach((()=>checkCollect=!1)));const mockInstanceConfig=(e,o,t,r)=>{var n;const s=ng_mocks_universe.A.configInstance.has(e)?ng_mocks_universe.A.configInstance.get(e):{},c=s.overloads||[];c.push([o,t,r]),s.overloads=c,ng_mocks_universe.A.configInstance.set(e,Object.assign({},s));const _=null!==(n=currentStack.mockInstance)&&void 0!==n?n:[];return _.push(e),currentStack.mockInstance=_,checkCollect&&checkReset.push([e,ng_mocks_universe.A.configInstance.get(e),currentStack]),t};function MockInstance(e,...o){if(func_import_exists(e,"MockInstance"),o.length>0){const{key:t,value:r,accessor:n}=parseMockInstanceArgs(o);return mockInstanceConfig(e,t,r,n)}const t=ng_mocks_universe.A.configInstance.get(e)||{};ng_mocks_universe.A.configInstance.set(e,Object.assign(Object.assign({},t),{overloads:[]}));for(let o=checkReset.length-1;o>=0;o-=1)checkReset[o][0]===e&&checkReset[o][2]===currentStack&&checkReset.splice(o,1)}function MockReset(){ng_mocks_universe.A.configInstance.clear()}function MockDeclarations(...e){return e.map(MockDeclaration)}function MockDeclaration(e){if((0,func_is_ng_def.p)(e,"p"))return MockPipe(e);if((0,func_is_ng_def.p)(e,"c"))return MockComponent(e);if((0,func_is_ng_def.p)(e,"d"))return MockDirective(e);throw error_jest_mock(e),new Error(["MockDeclaration does not know how to mock","function"==typeof e?(0,func_get_name.A)(e):e].join(" "))}!function(e){e.remember=function(){ng_mocks_stack.stackPush()},e.restore=function(){ng_mocks_stack.stackPop()},e.scope=function(o="case"){"all"!==o&&"suite"!==o||(beforeAll(e.remember),afterAll(e.restore)),"all"!==o&&"case"!==o||(beforeEach(e.remember),afterEach(e.restore))}}(MockInstance||(MockInstance={}));const generateTemplateAttrWrap=(e,o)=>"i"===o?`[${e}]`:`(${e})`,generateTemplateAttrWithParams=(e,o)=>{let t=` ${generateTemplateAttrWrap(e,o)}="`;return t+="i"===o?e:`__ngMocksOutput('${e}', $event)`,t+='"',t},generateTemplateAttr=(e,o,t)=>{if(!e&&"o"===t)return"";let r="";const n=null!=e?e:o;for(const e of o){const{name:o,alias:s}=(0,func_directive_io_parse.A)(e);r+=-1===n.indexOf(s||o)?"":generateTemplateAttrWithParams(s||o,t)}return r},func_generate_template=(e,{selector:o,bindings:t,inputs:r,outputs:n})=>{let s="";return"string"==typeof e?s=e:(0,func_is_ng_def.p)(e,"p")&&t&&-1!==t.indexOf("$implicit")?s=`{{ $implicit | ${core_reflect_pipe_resolve(e).name} }}`:o&&(s+=`<${o}`,s+=generateTemplateAttr(t,r,"i"),s+=generateTemplateAttr(t,n,"o"),s+=`>`),s},generateWrapperOutput=e=>(o,t)=>"function"==typeof e[o]?e[o](t):e[o]&&"object"==typeof e[o]&&"function"==typeof e[o].emit?e[o].emit(t):e[o]&&"object"==typeof e[o]&&"function"==typeof e[o].next?e[o].next(t):void(e[o]=t),generateWrapperComponent=({bindings:e,options:o,inputs:t})=>{class r{constructor(){(0,core_define_property.A)(this,"__ngMocksOutput",generateWrapperOutput(this));let o=0;if((0,helper_define_property_descriptor.A)(this,"__ngContext__",{get:()=>o,set:e=>o=e,enumerable:!1}),!e)for(const e of t||[]){let o=null;(0,helper_define_property_descriptor.A)(this,e,{get:()=>o,set:e=>o=e})}}}return(0,core_define_property.A)(r.prototype,`__ngMocks_index_${ng_mocks_universe.A.index()}`,void 0,!1),(0,core_.Component)(o)(r),r},generateWrapperDirective=({selector:e,options:o})=>{class t{}return(0,core_.Directive)({selector:e,providers:o.providers})(t),t},getCache=()=>{var e;const o=null!==(e=ng_mocks_universe.A.config.get("MockRenderCaches"))&&void 0!==e?e:[];return 0===o.length&&ng_mocks_universe.A.config.set("MockRenderCaches",o),o},checkCache=(e,o)=>{for(const t of e){if(t.cacheKey.length!==o.length)continue;let e=!0;for(let r=0;r{var n,s,c;const _=getCache(),i=[e,...null!=t?t:[null],...null!==(n=r.providers)&&void 0!==n?n:[null],...null!==(s=r.viewProviders)&&void 0!==s?s:[null]];let a=checkCache(_,i);if(a)return a;const l=o.inputs?[...o.inputs]:[],p=o.outputs?[...o.outputs]:[];if(o.hostDirectives)for(const e of o.hostDirectives)"object"==typeof e&&e.directive&&(e.inputs&&l.push(...e.inputs),e.outputs&&p.push(...e.outputs));const u=func_generate_template(e,{selector:o.selector,inputs:l,outputs:p,bindings:t}),d={providers:r.providers,selector:"mock-render",template:u,viewProviders:r.viewProviders};if(a=generateWrapperComponent(Object.assign(Object.assign({},o),{bindings:t,options:d})),(0,core_define_property.A)(a,"cacheKey",i),(0,core_define_property.A)(a,"tpl",u),o.selector&&d.providers){const e=generateWrapperDirective(Object.assign(Object.assign({},o),{bindings:t,options:d}));(0,core_define_property.A)(a,"providers",e)}return _.unshift(a),_.splice(null!==(c=ng_mocks_universe.A.global.get("mockRenderCacheSize"))&&void 0!==c?c:core_config.A.mockRenderCacheSize),a},createPropertyGet=(e,o,t)=>{const r=()=>{if("function"==typeof t[e]){if(o[`__ngMocks_${e}__origin`]!==t[e]){const r=helper_mock_service.A.createClone(t[e],o,t);(0,core_define_property.A)(o,`__ngMocks_${e}`,r),(0,core_define_property.A)(o,`__ngMocks_${e}__origin`,t[e])}return o[`__ngMocks_${e}`]}return t[e]};return(0,core_define_property.A)(r,"__ngMocksProxy",!0),r},createPropertySet=(e,o,t)=>{const r=r=>{o[`__ngMocks_${e}`]&&(o[`__ngMocks_${e}`]=void 0),o[`__ngMocks_${e}__origin`]&&(o[`__ngMocks_${e}__origin`]=void 0),t[e]=r};return(0,core_define_property.A)(r,"__ngMocksProxy",!0),r},extractAllKeys=e=>[...helper_mock_service.A.extractPropertiesFromPrototype(Object.getPrototypeOf(e)),...helper_mock_service.A.extractMethodsFromPrototype(Object.getPrototypeOf(e)),...Object.keys(e)],extractOwnKeys=e=>[...Object.getOwnPropertyNames(e),...Object.keys(e)],func_install_prop_reader=(e,o,t,r=!1)=>{if(!o)return;(0,core_define_property.A)(e,"__ngMocks__source",o);const n=extractOwnKeys(e),s=[...extractAllKeys(o),...t];for(const t of s)(r||-1===n.indexOf(t))&&((0,helper_define_property_descriptor.A)(e,t,{get:createPropertyGet(t,e,o),set:createPropertySet(t,e,o)}),n.push(t))},registerTemplateMiddleware=(e,o)=>{const t=(0,core_helpers.He)(e),r={provide:e,useExisting:t};o.providers=[...o.providers||[],r];let n={};try{const t=testing_.TestBed.ngMocksOverrides,{override:s}=t.get(e);n=Object.assign({},s.set),n.providers=n.providers?[...n.providers,r]:o.providers}catch(e){}const s=!0===o.__ngMocksStandalone;((0,func_is_ng_def.p)(e,"c")?core_.Component:core_.Directive)(Object.assign(Object.assign(Object.assign({},o),n),s?{standalone:s}:{}))(t),testing_.TestBed.configureTestingModule({[s?"imports":"declarations"]:[t]})},func_reflect_template=e=>{var o;if(!(0,func_is_ng_def.p)(e,"c")&&!(0,func_is_ng_def.p)(e,"d"))return{};const t=core_reflect_directive_resolve(e),r={};for(const e of Object.keys(t))"standalone"!==e?r[e]=t[e]:(0,core_define_property.A)(r,"__ngMocksStandalone",!!t[e]);return r.selector&&/[\s,[\]]/.test(r.selector)&&(r.selector=""),r.selector||(r.selector=(null===(o=testing_.TestBed.ngMocksSelectors)||void 0===o?void 0:o.get(e))||"",r.selector||(r.selector=`ng-mocks-${e.name}`,registerTemplateMiddleware(e,r),testing_.TestBed.ngMocksSelectors&&testing_.TestBed.ngMocksSelectors.set(e,r.selector))),r},renderDeclaration=(e,o,t)=>{e.point=e.debugElement.children[0]&&"#text"!==e.debugElement.children[0].nativeElement.nodeName&&"#comment"!==e.debugElement.children[0].nativeElement.nodeName?e.debugElement.children[0]:e.debugElement,(0,func_is_ng_def.p)(o,"d")?(0,helper_define_property_descriptor.A)(e.point,"componentInstance",{get:()=>ngMocks.get(e.point,o)}):(0,func_is_ng_def.p)(o,"p")&&(0,helper_define_property_descriptor.A)(e.point,"componentInstance",{get:()=>ngMocks.findInstance(e.point,o)}),tryWhen(!t,(()=>func_install_prop_reader(e.componentInstance,e.point.componentInstance,[])))},renderInjection=(e,o,t)=>{let r;try{r=(0,core_helpers.Ah)(o)}catch(e){if((0,func_is_ng_def.p)(o,"p"))throw new Error([`Cannot render ${(0,func_get_name.A)(o)}.`,"Did you forget to set $implicit param, or add the pipe to providers?","https://ng-mocks.sudo.eu/guides/pipe"].join(" "));throw e}t&&ngMocks.stub(r,t),e.point=(0,mock_service.K)(core_.DebugElement,{childNodes:[],children:[],componentInstance:r,nativeElement:(0,mock_service.K)(HTMLElement)}),func_install_prop_reader(e.componentInstance,e.point.componentInstance,[],!0)},tryWhen=(e,o)=>{if(e)try{o()}catch(e){}},fixtureMessage=["Forgot to flush TestBed?","MockRender cannot be used without a reset after TestBed.get / TestBed.inject / TestBed.createComponent and another MockRender in the same test.","If you want to mock a service before rendering, consider usage of MockRenderFactory or MockInstance.","To flush TestBed, add a call of ngMocks.flushTestBed() before the call of MockRender, or pass `reset: true` to MockRender options."].join(" "),handleFixtureError=e=>{const o=new Error(fixtureMessage);throw(0,core_define_property.A)(o,"parent",e),o},flushTestBed=e=>{const o=ng_mocks_universe.A.global.get("flags"),t=(0,testing_.getTestBed)();e.reset||!t._instantiated&&!t._testModuleRef?ngMocks.flushTestBed():"throw"!==o.onTestBedFlushNeed&&(t._instantiated||t._testModuleRef)&&("warn"===o.onTestBedFlushNeed&&console.warn(fixtureMessage),ngMocks.flushTestBed())},generateFactoryInstall=(e,o)=>()=>{var t;const r=(0,testing_.getTestBed)(),n=(null===(t=r._compiler)||void 0===t?void 0:t.declarations)||r.declarations||r._declarations;if(!n||-1===n.indexOf(e)){flushTestBed(o);try{const o=[];e.providers&&o.push(e.providers),o.push(e),testing_.TestBed.configureTestingModule({declarations:o})}catch(e){handleFixtureError(e)}}},generateFactory=(e,o,t,r)=>{const n=(r,s)=>{n.configureTestBed();const c=testing_.TestBed.createComponent(e);return func_install_prop_reader(c.componentInstance,null!=r?r:{},null!=o?o:[]),(0,core_define_property.A)(c,"ngMocksStackId",ng_mocks_universe.A.global.get("bullet:stack:id")),(void 0===s||s)&&c.detectChanges(),"string"==typeof t||(0,func_is_ng_def.p)(t,"c")||(0,func_is_ng_def.p)(t,"d")||e.tpl&&(0,func_is_ng_def.p)(t,"p")?renderDeclaration(c,t,r):renderInjection(c,t,r),c};return n.declaration=e,n.bindings=o,n.configureTestBed=generateFactoryInstall(e,r),n};function MockRenderFactory(e,o,t={}){func_import_exists(e,"MockRender");const r="string"==typeof e||(0,func_is_ng_def.p)(e,"t")?{}:func_reflect_template(e),n=func_create_wrapper(e,r,o,t),s=generateFactory(n,o,e,t);return"root"!==ng_mocks_stack.current().level&&!1!==t.configureTestBed&&s.configureTestBed(),s}function MockRender(e,o,t=!0){const r=0===arguments.length?"":e,n=o&&"object"==typeof o?Object.keys(o):o,s="boolean"==typeof t?{detectChanges:t}:Object.assign({},t);return MockRenderFactory(r,n,s)(o,s.detectChanges)}var __webpack_exports__IMockBuilder=__webpack_exports__.rX,__webpack_exports__IMockBuilderConfig=__webpack_exports__.HA,__webpack_exports__IMockBuilderConfigAll=__webpack_exports__.Wn,__webpack_exports__IMockBuilderConfigComponent=__webpack_exports__.Me,__webpack_exports__IMockBuilderConfigDirective=__webpack_exports__.I7,__webpack_exports__IMockBuilderConfigModule=__webpack_exports__.tv,__webpack_exports__IMockBuilderExtended=__webpack_exports__.OG,__webpack_exports__IMockBuilderProvider=__webpack_exports__.sF,__webpack_exports__IMockBuilderResult=__webpack_exports__.me,__webpack_exports__LegacyControlValueAccessor=__webpack_exports__.bk,__webpack_exports__Mock=__webpack_exports__.JT,__webpack_exports__MockBuilder=__webpack_exports__._V,__webpack_exports__MockComponent=__webpack_exports__.Am,__webpack_exports__MockComponents=__webpack_exports__.$u,__webpack_exports__MockControlValueAccessor=__webpack_exports__.D2,__webpack_exports__MockDeclaration=__webpack_exports__.dw,__webpack_exports__MockDeclarations=__webpack_exports__.uV,__webpack_exports__MockDirective=__webpack_exports__.Cc,__webpack_exports__MockDirectives=__webpack_exports__.nr,__webpack_exports__MockInstance=__webpack_exports__.Wm,__webpack_exports__MockModule=__webpack_exports__.BN,__webpack_exports__MockPipe=__webpack_exports__.ZJ,__webpack_exports__MockPipes=__webpack_exports__.yI,__webpack_exports__MockProvider=__webpack_exports__.Qo,__webpack_exports__MockProviders=__webpack_exports__.dD,__webpack_exports__MockRender=__webpack_exports__.Ty,__webpack_exports__MockRenderFactory=__webpack_exports__.P0,__webpack_exports__MockReset=__webpack_exports__.mB,__webpack_exports__MockService=__webpack_exports__.KH,__webpack_exports__MockValidator=__webpack_exports__.b6,__webpack_exports__MockedComponent=__webpack_exports__.Je,__webpack_exports__MockedDirective=__webpack_exports__.T4,__webpack_exports__MockedModule=__webpack_exports__.qZ,__webpack_exports__MockedPipe=__webpack_exports__.cm,__webpack_exports__NG_MOCKS=__webpack_exports__.en,__webpack_exports__NG_MOCKS_GUARDS=__webpack_exports__.ZG,__webpack_exports__NG_MOCKS_INTERCEPTORS=__webpack_exports__.rO,__webpack_exports__NG_MOCKS_OVERRIDES=__webpack_exports__.So,__webpack_exports__NG_MOCKS_RESOLVERS=__webpack_exports__.C3,__webpack_exports__NG_MOCKS_ROOT_PROVIDERS=__webpack_exports__.gG,__webpack_exports__NG_MOCKS_TOUCHES=__webpack_exports__.Em,__webpack_exports__getInjection=__webpack_exports__.Ah,__webpack_exports__getMockedNgDefOf=__webpack_exports__.xz,__webpack_exports__getSourceOfMock=__webpack_exports__.VK,__webpack_exports__getTestBedInjection=__webpack_exports__.d5,__webpack_exports__isMockControlValueAccessor=__webpack_exports__.IA,__webpack_exports__isMockNgDef=__webpack_exports__.Bt,__webpack_exports__isMockOf=__webpack_exports__.AW,__webpack_exports__isMockValidator=__webpack_exports__.lt,__webpack_exports__isMockedNgDefOf=__webpack_exports__.Fk,__webpack_exports__isNgDef=__webpack_exports__.pA,__webpack_exports__isNgInjectionToken=__webpack_exports__.SM,__webpack_exports__ngMocks=__webpack_exports__.H5;export{__webpack_exports__IMockBuilder as IMockBuilder,__webpack_exports__IMockBuilderConfig as IMockBuilderConfig,__webpack_exports__IMockBuilderConfigAll as IMockBuilderConfigAll,__webpack_exports__IMockBuilderConfigComponent as IMockBuilderConfigComponent,__webpack_exports__IMockBuilderConfigDirective as IMockBuilderConfigDirective,__webpack_exports__IMockBuilderConfigModule as IMockBuilderConfigModule,__webpack_exports__IMockBuilderExtended as IMockBuilderExtended,__webpack_exports__IMockBuilderProvider as IMockBuilderProvider,__webpack_exports__IMockBuilderResult as IMockBuilderResult,__webpack_exports__LegacyControlValueAccessor as LegacyControlValueAccessor,__webpack_exports__Mock as Mock,__webpack_exports__MockBuilder as MockBuilder,__webpack_exports__MockComponent as MockComponent,__webpack_exports__MockComponents as MockComponents,__webpack_exports__MockControlValueAccessor as MockControlValueAccessor,__webpack_exports__MockDeclaration as MockDeclaration,__webpack_exports__MockDeclarations as MockDeclarations,__webpack_exports__MockDirective as MockDirective,__webpack_exports__MockDirectives as MockDirectives,__webpack_exports__MockInstance as MockInstance,__webpack_exports__MockModule as MockModule,__webpack_exports__MockPipe as MockPipe,__webpack_exports__MockPipes as MockPipes,__webpack_exports__MockProvider as MockProvider,__webpack_exports__MockProviders as MockProviders,__webpack_exports__MockRender as MockRender,__webpack_exports__MockRenderFactory as MockRenderFactory,__webpack_exports__MockReset as MockReset,__webpack_exports__MockService as MockService,__webpack_exports__MockValidator as MockValidator,__webpack_exports__MockedComponent as MockedComponent,__webpack_exports__MockedDirective as MockedDirective,__webpack_exports__MockedModule as MockedModule,__webpack_exports__MockedPipe as MockedPipe,__webpack_exports__NG_MOCKS as NG_MOCKS,__webpack_exports__NG_MOCKS_GUARDS as NG_MOCKS_GUARDS,__webpack_exports__NG_MOCKS_INTERCEPTORS as NG_MOCKS_INTERCEPTORS,__webpack_exports__NG_MOCKS_OVERRIDES as NG_MOCKS_OVERRIDES,__webpack_exports__NG_MOCKS_RESOLVERS as NG_MOCKS_RESOLVERS,__webpack_exports__NG_MOCKS_ROOT_PROVIDERS as NG_MOCKS_ROOT_PROVIDERS,__webpack_exports__NG_MOCKS_TOUCHES as NG_MOCKS_TOUCHES,__webpack_exports__getInjection as getInjection,__webpack_exports__getMockedNgDefOf as getMockedNgDefOf,__webpack_exports__getSourceOfMock as getSourceOfMock,__webpack_exports__getTestBedInjection as getTestBedInjection,__webpack_exports__isMockControlValueAccessor as isMockControlValueAccessor,__webpack_exports__isMockNgDef as isMockNgDef,__webpack_exports__isMockOf as isMockOf,__webpack_exports__isMockValidator as isMockValidator,__webpack_exports__isMockedNgDefOf as isMockedNgDefOf,__webpack_exports__isNgDef as isNgDef,__webpack_exports__isNgInjectionToken as isNgInjectionToken,__webpack_exports__ngMocks as ngMocks}; + //# sourceMappingURL=index.mjs.map diff --git a/proxy.conf.mjs b/proxy.conf.mjs index 9a1ecebc4a3d..53e4497d75ea 100644 --- a/proxy.conf.mjs +++ b/proxy.conf.mjs @@ -2,22 +2,23 @@ export default [ { context: [ - "/api", - "/services", - "/management", - "/swagger-resources", - "/v3/api-docs", - "/h2-console", - "/auth", - "/health", - "/public", - "/.well-known" + "/api/", + "/services/", + "/management/", + "^/management$", + "/swagger-resources/", + "/v3/api-docs/", + "/h2-console/", + "/auth/", + "/health/", + "/public/", + "/.well-known/" ], target: `http://localhost:8080`, secure: false }, { - context: ["/websocket"], + context: ["/websocket/"], target: "ws://127.0.0.1:8080", ws: true } diff --git a/src/main/webapp/app/account/activate/activate.component.ts b/src/main/webapp/app/account/activate/activate.component.ts index a05a908b695d..10ec25c6ad93 100644 --- a/src/main/webapp/app/account/activate/activate.component.ts +++ b/src/main/webapp/app/account/activate/activate.component.ts @@ -9,7 +9,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-activate', templateUrl: './activate.component.html', - standalone: true, imports: [TranslateDirective, RouterLink, ArtemisSharedModule], }) export class ActivateComponent implements OnInit { diff --git a/src/main/webapp/app/account/password-reset/external/external-user-password-reset-modal.component.ts b/src/main/webapp/app/account/password-reset/external/external-user-password-reset-modal.component.ts index d122291f564d..3b5dd7ed9c7c 100644 --- a/src/main/webapp/app/account/password-reset/external/external-user-password-reset-modal.component.ts +++ b/src/main/webapp/app/account/password-reset/external/external-user-password-reset-modal.component.ts @@ -6,7 +6,6 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; @Component({ selector: 'jhi-external-user-password-reset-modal', templateUrl: './external-user-password-reset-modal.component.html', - standalone: true, imports: [TranslateDirective, ArtemisSharedModule], }) export class ExternalUserPasswordResetModalComponent { diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts index 35cbb5f0483c..a5aeac5c6e03 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts @@ -11,7 +11,6 @@ import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @Component({ selector: 'jhi-password-reset-finish', templateUrl: './password-reset-finish.component.html', - standalone: true, imports: [TranslateDirective, RouterLink, FormsModule, ReactiveFormsModule, PasswordStrengthBarComponent, ArtemisSharedCommonModule, ArtemisSharedModule], }) export class PasswordResetFinishComponent implements OnInit, AfterViewInit { diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts index 3e245c20e2b6..5823df8e2f4e 100644 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts @@ -15,7 +15,6 @@ import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @Component({ selector: 'jhi-password-reset-init', templateUrl: './password-reset-init.component.html', - standalone: true, imports: [TranslateDirective, FormsModule, ArtemisSharedCommonModule, ArtemisSharedModule], }) export class PasswordResetInitComponent implements OnInit, AfterViewInit { diff --git a/src/main/webapp/app/account/password/password-strength-bar.component.ts b/src/main/webapp/app/account/password/password-strength-bar.component.ts index d09537aedab9..c3ae5cf17397 100644 --- a/src/main/webapp/app/account/password/password-strength-bar.component.ts +++ b/src/main/webapp/app/account/password/password-strength-bar.component.ts @@ -15,7 +15,6 @@ import { ArtemisSharedModule } from 'app/shared/shared.module';
    `, styleUrls: ['password-strength-bar.scss'], - standalone: true, imports: [TranslateDirective, ArtemisSharedModule], }) export class PasswordStrengthBarComponent { diff --git a/src/main/webapp/app/account/password/password.component.ts b/src/main/webapp/app/account/password/password.component.ts index b2a600dd97bb..4b614de2e14a 100644 --- a/src/main/webapp/app/account/password/password.component.ts +++ b/src/main/webapp/app/account/password/password.component.ts @@ -12,7 +12,6 @@ import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @Component({ selector: 'jhi-password', templateUrl: './password.component.html', - standalone: true, imports: [TranslateDirective, FormsModule, ReactiveFormsModule, PasswordStrengthBarComponent, ArtemisSharedCommonModule, ArtemisSharedModule], }) export class PasswordComponent implements OnInit { diff --git a/src/main/webapp/app/account/register/register.component.ts b/src/main/webapp/app/account/register/register.component.ts index 15ea460bfb73..a63fa4ba3901 100644 --- a/src/main/webapp/app/account/register/register.component.ts +++ b/src/main/webapp/app/account/register/register.component.ts @@ -15,7 +15,6 @@ import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @Component({ selector: 'jhi-register', templateUrl: './register.component.html', - standalone: true, imports: [TranslateDirective, FormsModule, ReactiveFormsModule, PasswordStrengthBarComponent, ArtemisSharedCommonModule, ArtemisSharedModule], }) export class RegisterComponent implements OnInit, AfterViewInit { diff --git a/src/main/webapp/app/account/settings/settings.component.ts b/src/main/webapp/app/account/settings/settings.component.ts index 75e9187aa1bf..a6d8c02835b8 100644 --- a/src/main/webapp/app/account/settings/settings.component.ts +++ b/src/main/webapp/app/account/settings/settings.component.ts @@ -12,7 +12,6 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; @Component({ selector: 'jhi-settings', templateUrl: './settings.component.html', - standalone: true, imports: [TranslateDirective, FormsModule, ReactiveFormsModule, ArtemisSharedCommonModule, ArtemisSharedModule], }) export class SettingsComponent implements OnInit { diff --git a/src/main/webapp/app/admin/admin.module.ts b/src/main/webapp/app/admin/admin.module.ts index 0a236665c96d..c3dff04c1312 100644 --- a/src/main/webapp/app/admin/admin.module.ts +++ b/src/main/webapp/app/admin/admin.module.ts @@ -1,5 +1,6 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; +import { ArtemisFormsModule } from 'app/forms/artemis-forms.module'; import { adminState } from './admin.route'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { FormDateTimePickerModule } from 'app/shared/date-time-picker/date-time-picker.module'; @@ -56,6 +57,7 @@ const ENTITY_STATES = [...adminState]; RouterModule.forChild(ENTITY_STATES), ArtemisSharedModule, FormDateTimePickerModule, + ArtemisFormsModule, NgxDatatableModule, ArtemisDataTableModule, ArtemisChartsModule, @@ -78,8 +80,6 @@ const ENTITY_STATES = [...adminState]; AdminImportStandardizedCompetenciesComponent, BuildAgentSummaryComponent, BuildAgentDetailsComponent, - ], - declarations: [ AuditsComponent, UserManagementComponent, UserManagementDetailComponent, diff --git a/src/main/webapp/app/admin/admin.route.ts b/src/main/webapp/app/admin/admin.route.ts index 992b20b48c2b..10b4e283d310 100644 --- a/src/main/webapp/app/admin/admin.route.ts +++ b/src/main/webapp/app/admin/admin.route.ts @@ -4,23 +4,12 @@ import { userManagementRoute } from 'app/admin/user-management/user-management.r import { systemNotificationManagementRoute } from 'app/admin/system-notification-management/system-notification-management.route'; import { Authority } from 'app/shared/constants/authority.constants'; import { upcomingExamsAndExercisesRoute } from 'app/admin/upcoming-exams-and-exercises/upcoming-exams-and-exercises.route'; -import { AuditsComponent } from 'app/admin/audits/audits.component'; -import { ConfigurationComponent } from 'app/admin/configuration/configuration.component'; -import { AdminFeatureToggleComponent } from 'app/admin/features/admin-feature-toggle.component'; -import { HealthComponent } from 'app/admin/health/health.component'; -import { LogsComponent } from 'app/admin/logs/logs.component'; -import { StatisticsComponent } from 'app/admin/statistics/statistics.component'; -import { DocsComponent } from 'app/admin/docs/docs.component'; + import { organizationMgmtRoute } from 'app/admin/organization-management/organization-management.route'; -import { MetricsComponent } from 'app/admin/metrics/metrics.component'; -import { BuildQueueComponent } from 'app/localci/build-queue/build-queue.component'; + import { LocalCIGuard } from 'app/localci/localci-guard.service'; import { ltiConfigurationRoute } from 'app/admin/lti-configuration/lti-configuration.route'; -import { BuildAgentSummaryComponent } from 'app/localci/build-agents/build-agent-summary/build-agent-summary.component'; -import { StandardizedCompetencyManagementComponent } from 'app/admin/standardized-competencies/standardized-competency-management.component'; -import { BuildAgentDetailsComponent } from 'app/localci/build-agents/build-agent-details/build-agent-details/build-agent-details.component'; -import { AdminImportStandardizedCompetenciesComponent } from 'app/admin/standardized-competencies/import/admin-import-standardized-competencies.component'; -import { CleanupServiceComponent } from 'app/admin/cleanup-service/cleanup-service.component'; + import { PendingChangesGuard } from 'app/shared/guard/pending-changes.guard'; export const adminState: Routes = [ @@ -33,7 +22,7 @@ export const adminState: Routes = [ children: [ { path: 'audits', - component: AuditsComponent, + loadComponent: () => import('app/admin/audits/audits.component').then((m) => m.AuditsComponent), data: { pageTitle: 'audits.title', defaultSort: 'auditEventDate,desc', @@ -41,56 +30,56 @@ export const adminState: Routes = [ }, { path: 'configuration', - component: ConfigurationComponent, + loadComponent: () => import('app/admin/configuration/configuration.component').then((m) => m.ConfigurationComponent), data: { pageTitle: 'configuration.title', }, }, { path: 'feature-toggles', - component: AdminFeatureToggleComponent, + loadComponent: () => import('app/admin/features/admin-feature-toggle.component').then((m) => m.AdminFeatureToggleComponent), data: { pageTitle: 'featureToggles.title', }, }, { path: 'health', - component: HealthComponent, + loadComponent: () => import('app/admin/health/health.component').then((m) => m.HealthComponent), data: { pageTitle: 'health.title', }, }, { path: 'logs', - component: LogsComponent, + loadComponent: () => import('app/admin/logs/logs.component').then((m) => m.LogsComponent), data: { pageTitle: 'logs.title', }, }, { path: 'docs', - component: DocsComponent, + loadComponent: () => import('app/admin/docs/docs.component').then((m) => m.DocsComponent), data: { pageTitle: 'global.menu.admin.apidocs', }, }, { path: 'metrics', - component: MetricsComponent, + loadComponent: () => import('app/admin/metrics/metrics.component').then((m) => m.MetricsComponent), data: { pageTitle: 'metrics.title', }, }, { path: 'user-statistics', - component: StatisticsComponent, + loadComponent: () => import('app/admin/statistics/statistics.component').then((m) => m.StatisticsComponent), data: { pageTitle: 'statistics.title', }, }, { path: 'build-queue', - component: BuildQueueComponent, + loadComponent: () => import('app/localci/build-queue/build-queue.component').then((m) => m.BuildQueueComponent), data: { pageTitle: 'artemisApp.buildQueue.title', }, @@ -98,7 +87,7 @@ export const adminState: Routes = [ }, { path: 'build-agents', - component: BuildAgentSummaryComponent, + loadComponent: () => import('app/localci/build-agents/build-agent-summary/build-agent-summary.component').then((m) => m.BuildAgentSummaryComponent), data: { pageTitle: 'artemisApp.buildAgents.title', }, @@ -106,7 +95,8 @@ export const adminState: Routes = [ }, { path: 'build-agents/details', - component: BuildAgentDetailsComponent, + loadComponent: () => + import('app/localci/build-agents/build-agent-details/build-agent-details/build-agent-details.component').then((m) => m.BuildAgentDetailsComponent), data: { pageTitle: 'artemisApp.buildAgents.title', }, @@ -114,7 +104,8 @@ export const adminState: Routes = [ }, { path: 'standardized-competencies', - component: StandardizedCompetencyManagementComponent, + loadComponent: () => + import('app/admin/standardized-competencies/standardized-competency-management.component').then((m) => m.StandardizedCompetencyManagementComponent), data: { pageTitle: 'artemisApp.standardizedCompetency.title', }, @@ -129,7 +120,10 @@ export const adminState: Routes = [ children: [ { path: 'import', - component: AdminImportStandardizedCompetenciesComponent, + loadComponent: () => + import('app/admin/standardized-competencies/import/admin-import-standardized-competencies.component').then( + (m) => m.AdminImportStandardizedCompetenciesComponent, + ), data: { pageTitle: 'artemisApp.standardizedCompetency.import.title', }, @@ -153,7 +147,7 @@ export const adminState: Routes = [ }, { path: 'cleanup-service', - component: CleanupServiceComponent, + loadComponent: () => import('app/admin/cleanup-service/cleanup-service.component').then((m) => m.CleanupServiceComponent), data: { pageTitle: 'cleanupService.title', }, diff --git a/src/main/webapp/app/admin/audits/audits.component.ts b/src/main/webapp/app/admin/audits/audits.component.ts index 7f98a7fbe09f..9422cfc0176a 100644 --- a/src/main/webapp/app/admin/audits/audits.component.ts +++ b/src/main/webapp/app/admin/audits/audits.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { HttpHeaders, HttpResponse } from '@angular/common/http'; import { DatePipe } from '@angular/common'; import { ActivatedRoute, Router } from '@angular/router'; @@ -8,12 +8,26 @@ import { ITEMS_PER_PAGE } from 'app/shared/constants/pagination.constants'; import { Audit } from './audit.model'; import { AuditsService } from './audits.service'; import { faSort } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ItemCountComponent } from 'app/shared/pagination/item-count.component'; +import { NgbPagination } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-audit', templateUrl: './audits.component.html', + imports: [TranslateDirective, FormsModule, SortDirective, SortByDirective, FaIconComponent, ItemCountComponent, NgbPagination, ArtemisDatePipe], }) export class AuditsComponent implements OnInit { + private auditsService = inject(AuditsService); + private activatedRoute = inject(ActivatedRoute); + private datePipe = inject(DatePipe); + private router = inject(Router); + audits?: Audit[]; fromDate = ''; predicate!: string; @@ -31,13 +45,6 @@ export class AuditsComponent implements OnInit { // Icon faSort = faSort; - constructor( - private auditsService: AuditsService, - private activatedRoute: ActivatedRoute, - private datePipe: DatePipe, - private router: Router, - ) {} - ngOnInit(): void { this.toDate = this.today(); this.fromDate = this.previousMonth(); diff --git a/src/main/webapp/app/admin/audits/audits.service.ts b/src/main/webapp/app/admin/audits/audits.service.ts index cfefe048cf85..e72793f459e4 100644 --- a/src/main/webapp/app/admin/audits/audits.service.ts +++ b/src/main/webapp/app/admin/audits/audits.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { createRequestOption } from 'app/shared/util/request.util'; @@ -6,7 +6,7 @@ import { Audit } from 'app/admin/audits/audit.model'; @Injectable({ providedIn: 'root' }) export class AuditsService { - constructor(private http: HttpClient) {} + private http = inject(HttpClient); query(req: any): Observable> { const params: HttpParams = createRequestOption(req); diff --git a/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.ts b/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.ts index be398b021798..34d948f59464 100644 --- a/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.ts +++ b/src/main/webapp/app/admin/cleanup-service/cleanup-operation-modal.component.ts @@ -11,7 +11,6 @@ import { faCheckCircle, faTimes } from '@fortawesome/free-solid-svg-icons'; @Component({ selector: 'jhi-cleanup-operation-modal', templateUrl: './cleanup-operation-modal.component.html', - standalone: true, imports: [TranslateDirective, ArtemisSharedModule], }) export class CleanupOperationModalComponent implements OnInit { diff --git a/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.ts b/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.ts index 00a6965915d5..5a8d663607e6 100644 --- a/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.ts +++ b/src/main/webapp/app/admin/cleanup-service/cleanup-service.component.ts @@ -14,7 +14,6 @@ import { CleanupOperationModalComponent } from 'app/admin/cleanup-service/cleanu @Component({ selector: 'jhi-cleanup-service', templateUrl: './cleanup-service.component.html', - standalone: true, imports: [FormDateTimePickerModule, ArtemisSharedModule, ArtemisSharedComponentModule], }) export class CleanupServiceComponent implements OnInit { diff --git a/src/main/webapp/app/admin/configuration/configuration.component.ts b/src/main/webapp/app/admin/configuration/configuration.component.ts index 9888e69646ac..6beb882fab6d 100644 --- a/src/main/webapp/app/admin/configuration/configuration.component.ts +++ b/src/main/webapp/app/admin/configuration/configuration.component.ts @@ -1,14 +1,23 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ConfigurationService } from './configuration.service'; import { Bean, PropertySource } from './configuration.model'; import { faSort } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { JsonPipe, KeyValuePipe } from '@angular/common'; @Component({ selector: 'jhi-configuration', templateUrl: './configuration.component.html', + imports: [TranslateDirective, FormsModule, SortDirective, SortByDirective, FaIconComponent, JsonPipe, KeyValuePipe], }) export class ConfigurationComponent implements OnInit { + private configurationService = inject(ConfigurationService); + allBeans!: Bean[]; beans: Bean[] = []; beansFilter = ''; @@ -18,8 +27,6 @@ export class ConfigurationComponent implements OnInit { // Icons faSort = faSort; - constructor(private configurationService: ConfigurationService) {} - ngOnInit(): void { this.configurationService.getBeans().subscribe((beans) => { this.allBeans = beans; diff --git a/src/main/webapp/app/admin/configuration/configuration.service.ts b/src/main/webapp/app/admin/configuration/configuration.service.ts index 8705370fbf44..7863392edd10 100644 --- a/src/main/webapp/app/admin/configuration/configuration.service.ts +++ b/src/main/webapp/app/admin/configuration/configuration.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -7,7 +7,7 @@ import { Bean, Beans, ConfigProps, Env, PropertySource } from './configuration.m @Injectable({ providedIn: 'root' }) export class ConfigurationService { - constructor(private http: HttpClient) {} + private http = inject(HttpClient); getBeans(): Observable { return this.http.get('management/configprops').pipe( diff --git a/src/main/webapp/app/admin/features/admin-feature-toggle.component.ts b/src/main/webapp/app/admin/features/admin-feature-toggle.component.ts index 185801509cc9..998b8f4aec19 100644 --- a/src/main/webapp/app/admin/features/admin-feature-toggle.component.ts +++ b/src/main/webapp/app/admin/features/admin-feature-toggle.component.ts @@ -1,6 +1,7 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { FeatureToggle, FeatureToggleService } from 'app/shared/feature-toggle/feature-toggle.service'; import { tap } from 'rxjs/operators'; +import { NgxDatatableModule } from '@siemens/ngx-datatable'; type FeatureToggleState = { index: number; @@ -24,11 +25,12 @@ type FeatureToggleState = { `, + imports: [NgxDatatableModule], }) export class AdminFeatureToggleComponent implements OnInit { - public availableToggles: FeatureToggleState[] = []; + private featureToggleService = inject(FeatureToggleService); - constructor(private featureToggleService: FeatureToggleService) {} + public availableToggles: FeatureToggleState[] = []; ngOnInit(): void { this.featureToggleService diff --git a/src/main/webapp/app/admin/health/health-modal.component.ts b/src/main/webapp/app/admin/health/health-modal.component.ts index 37efaebe3c34..62b69c7e82f2 100644 --- a/src/main/webapp/app/admin/health/health-modal.component.ts +++ b/src/main/webapp/app/admin/health/health-modal.component.ts @@ -1,15 +1,19 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { HealthDetails, HealthKey } from 'app/admin/health/health.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { KeyValuePipe } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-health-modal', templateUrl: './health-modal.component.html', + imports: [TranslateDirective, KeyValuePipe, ArtemisTranslatePipe], }) export class HealthModalComponent { - health?: { key: HealthKey; value: HealthDetails }; + private activeModal = inject(NgbActiveModal); - constructor(private activeModal: NgbActiveModal) {} + health?: { key: HealthKey; value: HealthDetails }; readableValue(value: any): string { if (this.health?.key === 'diskSpace') { diff --git a/src/main/webapp/app/admin/health/health.component.ts b/src/main/webapp/app/admin/health/health.component.ts index d07c215f787a..f2e3eba43a7a 100644 --- a/src/main/webapp/app/admin/health/health.component.ts +++ b/src/main/webapp/app/admin/health/health.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -6,23 +6,27 @@ import { HealthService } from './health.service'; import { HealthModalComponent } from './health-modal.component'; import { Health, HealthDetails, HealthStatus } from 'app/admin/health/health.model'; import { faEye, faSync } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { KeyValuePipe, NgClass } from '@angular/common'; +import { JhiConnectionStatusComponent } from 'app/shared/connection-status/connection-status.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-health', templateUrl: './health.component.html', + imports: [TranslateDirective, FaIconComponent, NgClass, JhiConnectionStatusComponent, KeyValuePipe, ArtemisTranslatePipe], }) export class HealthComponent implements OnInit { + private modalService = inject(NgbModal); + private healthService = inject(HealthService); + health?: Health; // Icons faSync = faSync; faEye = faEye; - constructor( - private modalService: NgbModal, - private healthService: HealthService, - ) {} - ngOnInit() { this.refresh(); } diff --git a/src/main/webapp/app/admin/health/health.service.ts b/src/main/webapp/app/admin/health/health.service.ts index b89c85e3d215..d76710012e46 100644 --- a/src/main/webapp/app/admin/health/health.service.ts +++ b/src/main/webapp/app/admin/health/health.service.ts @@ -1,16 +1,13 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; +import { Health } from 'app/admin/health/health.model'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class HealthService { - separator: string; + private http = inject(HttpClient); - constructor(private http: HttpClient) { - this.separator = '.'; - } - - checkHealth(): Observable { - return this.http.get('management/health'); + checkHealth(): Observable { + return this.http.get('management/health'); } } diff --git a/src/main/webapp/app/admin/legal/legal-document-update-routing.module.ts b/src/main/webapp/app/admin/legal/legal-document-update-routing.module.ts index 3eca238a3e17..5facd71ddbb9 100644 --- a/src/main/webapp/app/admin/legal/legal-document-update-routing.module.ts +++ b/src/main/webapp/app/admin/legal/legal-document-update-routing.module.ts @@ -1,13 +1,12 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { LegalDocumentUpdateComponent } from 'app/admin/legal/legal-document-update.component'; import { Authority } from 'app/shared/constants/authority.constants'; const routes: Routes = [ { path: '', - component: LegalDocumentUpdateComponent, + loadComponent: () => import('app/admin/legal/legal-document-update.component').then((m) => m.LegalDocumentUpdateComponent), data: { authorities: [Authority.ADMIN], }, diff --git a/src/main/webapp/app/admin/legal/legal-document-update.component.ts b/src/main/webapp/app/admin/legal/legal-document-update.component.ts index 63541a0c97dc..53a56df95509 100644 --- a/src/main/webapp/app/admin/legal/legal-document-update.component.ts +++ b/src/main/webapp/app/admin/legal/legal-document-update.component.ts @@ -1,20 +1,31 @@ -import { AfterContentChecked, ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; +import { AfterContentChecked, ChangeDetectorRef, Component, OnInit, ViewChild, inject } from '@angular/core'; import { faBan, faCheckCircle, faCircleNotch, faExclamationTriangle, faSave } from '@fortawesome/free-solid-svg-icons'; import { LegalDocumentService } from 'app/shared/service/legal-document.service'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbModalRef, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { UnsavedChangesWarningComponent } from 'app/admin/legal/unsaved-changes-warning/unsaved-changes-warning.component'; import { LegalDocument, LegalDocumentLanguage, LegalDocumentType } from 'app/entities/legal-document.model'; import { ActivatedRoute } from '@angular/router'; import { Observable, tap } from 'rxjs'; import { JhiLanguageHelper } from 'app/core/language/language.helper'; import { MarkdownEditorHeight, MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ModePickerComponent } from 'app/exercises/shared/mode-picker/mode-picker.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-privacy-statement-update-component', styleUrls: ['./legal-document-update.component.scss'], templateUrl: './legal-document-update.component.html', + imports: [TranslateDirective, MarkdownEditorMonacoComponent, FaIconComponent, NgbTooltip, ModePickerComponent, ArtemisTranslatePipe], }) export class LegalDocumentUpdateComponent implements OnInit, AfterContentChecked { + private legalDocumentService = inject(LegalDocumentService); + private modalService = inject(NgbModal); + private route = inject(ActivatedRoute); + private languageHelper = inject(JhiLanguageHelper); + private changeDetectorRef = inject(ChangeDetectorRef); + readonly SUPPORTED_LANGUAGES: LegalDocumentLanguage[] = [LegalDocumentLanguage.GERMAN, LegalDocumentLanguage.ENGLISH]; readonly faBan = faBan; readonly faSave = faSave; @@ -41,14 +52,6 @@ export class LegalDocumentUpdateComponent implements OnInit, AfterContentChecked unsavedChangesWarning: NgbModalRef; titleKey: string; - constructor( - private legalDocumentService: LegalDocumentService, - private modalService: NgbModal, - private route: ActivatedRoute, - private languageHelper: JhiLanguageHelper, - private changeDetectorRef: ChangeDetectorRef, - ) {} - ngOnInit() { // Tap the URL to determine, if it's the imprint or the privacy statement // we need the parent URL, because the imprint and privacy statement are children of the admin component and their path is specified there because they are lazy loaded diff --git a/src/main/webapp/app/admin/legal/legal-update.module.ts b/src/main/webapp/app/admin/legal/legal-update.module.ts index 26660839ca8a..460998bbfff6 100644 --- a/src/main/webapp/app/admin/legal/legal-update.module.ts +++ b/src/main/webapp/app/admin/legal/legal-update.module.ts @@ -16,7 +16,7 @@ import { LegalDocumentUnsavedChangesWarningModule } from 'app/admin/legal/unsave ArtemisMarkdownEditorModule, ArtemisModePickerModule, LegalDocumentUnsavedChangesWarningModule, + LegalDocumentUpdateComponent, ], - declarations: [LegalDocumentUpdateComponent], }) export class LegalUpdateModule {} diff --git a/src/main/webapp/app/admin/legal/unsaved-changes-warning/legal-document-unsaved-changes-warning.module.ts b/src/main/webapp/app/admin/legal/unsaved-changes-warning/legal-document-unsaved-changes-warning.module.ts index 23d07213db6d..4bddb4e7a740 100644 --- a/src/main/webapp/app/admin/legal/unsaved-changes-warning/legal-document-unsaved-changes-warning.module.ts +++ b/src/main/webapp/app/admin/legal/unsaved-changes-warning/legal-document-unsaved-changes-warning.module.ts @@ -4,7 +4,6 @@ import { ArtemisSharedComponentModule } from 'app/shared/components/shared-compo import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @NgModule({ - imports: [ArtemisSharedComponentModule, ArtemisSharedCommonModule], - declarations: [UnsavedChangesWarningComponent], + imports: [ArtemisSharedComponentModule, ArtemisSharedCommonModule, UnsavedChangesWarningComponent], }) export class LegalDocumentUnsavedChangesWarningModule {} diff --git a/src/main/webapp/app/admin/legal/unsaved-changes-warning/unsaved-changes-warning.component.ts b/src/main/webapp/app/admin/legal/unsaved-changes-warning/unsaved-changes-warning.component.ts index 3314f37adf15..70f31dd4b621 100644 --- a/src/main/webapp/app/admin/legal/unsaved-changes-warning/unsaved-changes-warning.component.ts +++ b/src/main/webapp/app/admin/legal/unsaved-changes-warning/unsaved-changes-warning.component.ts @@ -1,16 +1,20 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ButtonComponent } from 'app/shared/components/button.component'; @Component({ selector: 'jhi-unsaved-changes-warning', templateUrl: './unsaved-changes-warning.component.html', + imports: [FormsModule, TranslateDirective, ButtonComponent], }) export class UnsavedChangesWarningComponent { + private activeModal = inject(NgbActiveModal); + @Input() textMessage: string; - constructor(private activeModal: NgbActiveModal) {} - /** * Closes the modal in which the warning is shown and discards the changes * diff --git a/src/main/webapp/app/admin/logs/logs.component.ts b/src/main/webapp/app/admin/logs/logs.component.ts index 8f24d4fed3fa..17862729e58c 100644 --- a/src/main/webapp/app/admin/logs/logs.component.ts +++ b/src/main/webapp/app/admin/logs/logs.component.ts @@ -1,14 +1,23 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { faSort } from '@fortawesome/free-solid-svg-icons'; import { Level, Log, LoggersResponse } from 'app/admin/logs/log.model'; import { LogsService } from 'app/admin/logs/logs.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass, SlicePipe } from '@angular/common'; @Component({ selector: 'jhi-logs', templateUrl: './logs.component.html', styleUrls: ['./logs.component.scss'], + imports: [TranslateDirective, FormsModule, SortDirective, SortByDirective, FaIconComponent, NgClass, SlicePipe], }) export class LogsComponent implements OnInit { + private logsService = inject(LogsService); + loggers?: Log[]; filteredAndOrderedLoggers?: Log[]; filter = ''; @@ -18,8 +27,6 @@ export class LogsComponent implements OnInit { // Icons faSort = faSort; - constructor(private logsService: LogsService) {} - /** * Subscribe to the logsService to retrieve all logs */ diff --git a/src/main/webapp/app/admin/logs/logs.service.ts b/src/main/webapp/app/admin/logs/logs.service.ts index 73b2ddd7635e..65f2cf6f26de 100644 --- a/src/main/webapp/app/admin/logs/logs.service.ts +++ b/src/main/webapp/app/admin/logs/logs.service.ts @@ -1,11 +1,11 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { Level, LoggersResponse } from './log.model'; @Injectable({ providedIn: 'root' }) export class LogsService { - constructor(private http: HttpClient) {} + private http = inject(HttpClient); changeLevel(name: string, configuredLevel: Level): Observable { return this.http.post(`management/loggers/${name}`, { configuredLevel }); diff --git a/src/main/webapp/app/admin/lti-configuration/edit-lti-configuration.component.ts b/src/main/webapp/app/admin/lti-configuration/edit-lti-configuration.component.ts index 6f65f9c87969..f130ccf06b82 100644 --- a/src/main/webapp/app/admin/lti-configuration/edit-lti-configuration.component.ts +++ b/src/main/webapp/app/admin/lti-configuration/edit-lti-configuration.component.ts @@ -2,14 +2,18 @@ import { AlertService } from 'app/core/util/alert.service'; import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { finalize } from 'rxjs'; -import { FormControl, FormGroup } from '@angular/forms'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { faBan, faPlus, faSave } from '@fortawesome/free-solid-svg-icons'; import { LtiPlatformConfiguration } from 'app/admin/lti-configuration/lti-configuration.model'; import { LtiConfigurationService } from 'app/admin/lti-configuration/lti-configuration.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-edit-lti-configuration', templateUrl: './edit-lti-configuration.component.html', + imports: [FormsModule, ReactiveFormsModule, TranslateDirective, HelpIconComponent, FaIconComponent], }) export class EditLtiConfigurationComponent implements OnInit { private route = inject(ActivatedRoute); diff --git a/src/main/webapp/app/admin/lti-configuration/lti-configuration.component.ts b/src/main/webapp/app/admin/lti-configuration/lti-configuration.component.ts index a18fbb8f602c..cf7cbb55d0ea 100644 --- a/src/main/webapp/app/admin/lti-configuration/lti-configuration.component.ts +++ b/src/main/webapp/app/admin/lti-configuration/lti-configuration.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, inject } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { Course } from 'app/entities/course.model'; import { faExclamationTriangle, faPencilAlt, faPlus, faSort, faTrash, faWrench } from '@fortawesome/free-solid-svg-icons'; import { LtiPlatformConfiguration } from 'app/admin/lti-configuration/lti-configuration.model'; @@ -11,10 +11,39 @@ import { AlertService } from 'app/core/util/alert.service'; import { LTI_URLS } from 'app/admin/lti-configuration/lti-configuration.urls'; import { ITEMS_PER_PAGE } from 'app/shared/constants/pagination.constants'; import { combineLatest } from 'rxjs'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbNav, NgbNavContent, NgbNavItem, NgbNavLink, NgbNavLinkBase, NgbNavOutlet, NgbPagination } from '@ng-bootstrap/ng-bootstrap'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { CopyIconButtonComponent } from 'app/shared/components/copy-icon-button/copy-icon-button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ItemCountComponent } from 'app/shared/pagination/item-count.component'; @Component({ selector: 'jhi-lti-configuration', templateUrl: './lti-configuration.component.html', + imports: [ + FormsModule, + TranslateDirective, + NgbNav, + NgbNavItem, + NgbNavLink, + NgbNavLinkBase, + NgbNavContent, + HelpIconComponent, + CopyIconButtonComponent, + RouterLink, + FaIconComponent, + SortDirective, + SortByDirective, + DeleteButtonDirective, + ItemCountComponent, + NgbPagination, + NgbNavOutlet, + ], }) export class LtiConfigurationComponent implements OnInit { private router = inject(Router); diff --git a/src/main/webapp/app/admin/lti-configuration/lti-configuration.route.ts b/src/main/webapp/app/admin/lti-configuration/lti-configuration.route.ts index aa663034e8bf..b4d81dd451d5 100644 --- a/src/main/webapp/app/admin/lti-configuration/lti-configuration.route.ts +++ b/src/main/webapp/app/admin/lti-configuration/lti-configuration.route.ts @@ -1,13 +1,12 @@ import { Routes } from '@angular/router'; -import { LtiConfigurationComponent } from 'app/admin/lti-configuration/lti-configuration.component'; -import { EditLtiConfigurationComponent } from 'app/admin/lti-configuration/edit-lti-configuration.component'; + import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; export const ltiConfigurationRoute: Routes = [ { path: 'lti-configuration', - component: LtiConfigurationComponent, + loadComponent: () => import('app/admin/lti-configuration/lti-configuration.component').then((m) => m.LtiConfigurationComponent), data: { pageTitle: 'global.menu.admin.lti', defaultSort: 'id,desc', @@ -21,7 +20,7 @@ export const ltiConfigurationRoute: Routes = [ children: [ { path: 'new', - component: EditLtiConfigurationComponent, + loadComponent: () => import('app/admin/lti-configuration/edit-lti-configuration.component').then((m) => m.EditLtiConfigurationComponent), data: { authorities: [Authority.ADMIN], pageTitle: 'artemisApp.lti.addOrEditLtiPlatform', @@ -36,7 +35,7 @@ export const ltiConfigurationRoute: Routes = [ children: [ { path: 'edit', - component: EditLtiConfigurationComponent, + loadComponent: () => import('app/admin/lti-configuration/edit-lti-configuration.component').then((m) => m.EditLtiConfigurationComponent), data: { authorities: [Authority.ADMIN], pageTitle: 'artemisApp.lti.addOrEditLtiPlatform', diff --git a/src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.ts b/src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.ts index 7c3866f15985..46b8be5cdff0 100644 --- a/src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.ts +++ b/src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.ts @@ -9,7 +9,6 @@ import { DecimalPipe, KeyValuePipe } from '@angular/common'; selector: 'jhi-jvm-memory', templateUrl: './jvm-memory.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [TranslateDirective, NgbProgressbar, DecimalPipe, KeyValuePipe], }) export class JvmMemoryComponent { diff --git a/src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.ts b/src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.ts index 9a087bdb855b..824ba0fa519b 100644 --- a/src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.ts +++ b/src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.ts @@ -10,7 +10,6 @@ import { DecimalPipe } from '@angular/common'; selector: 'jhi-jvm-threads', templateUrl: './jvm-threads.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [TranslateDirective, NgbProgressbar, DecimalPipe], }) export class JvmThreadsComponent { diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.ts index 83b5d464abe8..eb5320fcafde 100644 --- a/src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.ts +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.ts @@ -8,7 +8,6 @@ import { DecimalPipe, KeyValuePipe } from '@angular/common'; selector: 'jhi-metrics-cache', templateUrl: './metrics-cache.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [TranslateDirective, DecimalPipe, KeyValuePipe], }) export class MetricsCacheComponent { diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.ts index 7c12eec1f89d..d8ff9cb81260 100644 --- a/src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.ts +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.ts @@ -8,7 +8,6 @@ import { DecimalPipe } from '@angular/common'; selector: 'jhi-metrics-datasource', templateUrl: './metrics-datasource.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [TranslateDirective, DecimalPipe], }) export class MetricsDatasourceComponent { diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.ts index d4ac3e797630..85653844f7ec 100644 --- a/src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.ts +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.ts @@ -6,7 +6,6 @@ import { DecimalPipe, KeyValuePipe } from '@angular/common'; selector: 'jhi-metrics-endpoints-requests', templateUrl: './metrics-endpoints-requests.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [DecimalPipe, KeyValuePipe], }) export class MetricsEndpointsRequestsComponent { diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.ts index e6482f229533..5fe766c0ba20 100644 --- a/src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.ts +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.ts @@ -8,7 +8,6 @@ import { DecimalPipe } from '@angular/common'; selector: 'jhi-metrics-garbagecollector', templateUrl: './metrics-garbagecollector.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [TranslateDirective, NgbProgressbar, DecimalPipe], }) export class MetricsGarbageCollectorComponent { diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.ts index 202afdafae93..2ef85a0d981a 100644 --- a/src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.ts +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.ts @@ -1,18 +1,17 @@ +import { NgClass } from '@angular/common'; import { ChangeDetectionStrategy, Component, OnInit, inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { faCheck } from '@fortawesome/free-solid-svg-icons'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { Thread, ThreadState } from '../../metrics.model'; import { TranslateDirective } from 'app/shared/language/translate.directive'; -import { FaIconComponent } from '@fortawesome/angular-fontawesome'; -import { FormsModule } from '@angular/forms'; -import { NgClass } from '@angular/common'; import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; +import { Thread, ThreadState } from '../../metrics.model'; @Component({ selector: 'jhi-thread-modal', templateUrl: './metrics-modal-threads.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [TranslateDirective, FaIconComponent, FormsModule, NgClass, ArtemisSharedCommonModule], }) export class MetricsModalThreadsComponent implements OnInit { diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.ts index bcb58fcb5775..138fa79ecdca 100644 --- a/src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.ts +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.ts @@ -9,7 +9,6 @@ import { DecimalPipe, KeyValuePipe } from '@angular/common'; selector: 'jhi-metrics-request', templateUrl: './metrics-request.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [TranslateDirective, NgbProgressbar, DecimalPipe, KeyValuePipe], }) export class MetricsRequestComponent { diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.ts index 358eff4855ff..e55208f186d4 100644 --- a/src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.ts +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.ts @@ -7,7 +7,6 @@ import { DatePipe, DecimalPipe } from '@angular/common'; selector: 'jhi-metrics-system', templateUrl: './metrics-system.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [NgbProgressbar, DecimalPipe, DatePipe], }) export class MetricsSystemComponent { diff --git a/src/main/webapp/app/admin/metrics/metrics.component.ts b/src/main/webapp/app/admin/metrics/metrics.component.ts index 3cbe7f9f4b79..2d0197c67ca8 100644 --- a/src/main/webapp/app/admin/metrics/metrics.component.ts +++ b/src/main/webapp/app/admin/metrics/metrics.component.ts @@ -19,7 +19,6 @@ import { MetricsDatasourceComponent } from './blocks/metrics-datasource/metrics- selector: 'jhi-metrics', templateUrl: './metrics.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [ TranslateDirective, FaIconComponent, diff --git a/src/main/webapp/app/admin/organization-management/organization-count-dto.model.ts b/src/main/webapp/app/admin/organization-management/organization-count-dto.model.ts index 522200ab572f..a1416ff07c46 100644 --- a/src/main/webapp/app/admin/organization-management/organization-count-dto.model.ts +++ b/src/main/webapp/app/admin/organization-management/organization-count-dto.model.ts @@ -2,6 +2,4 @@ export class OrganizationCountDto { public organizationId: number; public numberOfUsers: number; public numberOfCourses: number; - - constructor() {} } diff --git a/src/main/webapp/app/admin/organization-management/organization-management-detail.component.ts b/src/main/webapp/app/admin/organization-management/organization-management-detail.component.ts index 23f990728773..33b5532f77b7 100644 --- a/src/main/webapp/app/admin/organization-management/organization-management-detail.component.ts +++ b/src/main/webapp/app/admin/organization-management/organization-management-detail.component.ts @@ -1,5 +1,5 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, OnInit, ViewChild, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { HttpErrorResponse } from '@angular/common/http'; import { Organization } from 'app/entities/organization.model'; import { OrganizationManagementService } from 'app/admin/organization-management/organization-management.service'; @@ -12,6 +12,10 @@ import { iconsAsHTML } from 'app/utils/icons.utils'; import { UserService } from 'app/core/user/user.service'; import { DataTableComponent } from 'app/shared/data-table/data-table.component'; import { faUserSlash } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgxDatatableModule } from '@siemens/ngx-datatable'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; const cssClasses = { alreadyMember: 'already-member', @@ -21,8 +25,14 @@ const cssClasses = { @Component({ selector: 'jhi-organization-management-detail', templateUrl: './organization-management-detail.component.html', + imports: [TranslateDirective, RouterLink, DataTableComponent, NgxDatatableModule, FaIconComponent, DeleteButtonDirective], }) export class OrganizationManagementDetailComponent implements OnInit { + private organizationService = inject(OrganizationManagementService); + private userService = inject(UserService); + private alertService = inject(AlertService); + private route = inject(ActivatedRoute); + @ViewChild(DataTableComponent) dataTable: DataTableComponent; organization: Organization; @@ -42,13 +52,6 @@ export class OrganizationManagementDetailComponent implements OnInit { // Icons faUserSlash = faUserSlash; - constructor( - private organizationService: OrganizationManagementService, - private userService: UserService, - private alertService: AlertService, - private route: ActivatedRoute, - ) {} - /** * Retrieve the organization from the organization management activated route data subscription * and get the organization based on its id diff --git a/src/main/webapp/app/admin/organization-management/organization-management-resolve.service.ts b/src/main/webapp/app/admin/organization-management/organization-management-resolve.service.ts index 60277e2a3dc0..a9e42b5e99be 100644 --- a/src/main/webapp/app/admin/organization-management/organization-management-resolve.service.ts +++ b/src/main/webapp/app/admin/organization-management/organization-management-resolve.service.ts @@ -1,11 +1,11 @@ import { Organization } from 'app/entities/organization.model'; import { OrganizationManagementService } from 'app/admin/organization-management/organization-management.service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; @Injectable({ providedIn: 'root' }) export class OrganizationManagementResolve implements Resolve { - constructor(private organizationManagementService: OrganizationManagementService) {} + private organizationManagementService = inject(OrganizationManagementService); resolve(route: ActivatedRouteSnapshot) { if (route.params['id']) { diff --git a/src/main/webapp/app/admin/organization-management/organization-management-update.component.ts b/src/main/webapp/app/admin/organization-management/organization-management-update.component.ts index 4eace90dca4a..262fb7b30adb 100644 --- a/src/main/webapp/app/admin/organization-management/organization-management-update.component.ts +++ b/src/main/webapp/app/admin/organization-management/organization-management-update.component.ts @@ -1,14 +1,22 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Organization } from 'app/entities/organization.model'; import { OrganizationManagementService } from 'app/admin/organization-management/organization-management.service'; import { faBan, faSave } from '@fortawesome/free-solid-svg-icons'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CustomPatternValidatorDirective } from 'app/shared/validators/custom-pattern-validator.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-organization-management-update', templateUrl: './organization-management-update.component.html', + imports: [FormsModule, TranslateDirective, CustomPatternValidatorDirective, FaIconComponent], }) export class OrganizationManagementUpdateComponent implements OnInit { + private route = inject(ActivatedRoute); + private organizationService = inject(OrganizationManagementService); + organization: Organization; isSaving: boolean; @@ -16,11 +24,6 @@ export class OrganizationManagementUpdateComponent implements OnInit { faSave = faSave; faBan = faBan; - constructor( - private route: ActivatedRoute, - private organizationService: OrganizationManagementService, - ) {} - /** * Enable subscriptions to retrieve the organization based on the activated route on init */ diff --git a/src/main/webapp/app/admin/organization-management/organization-management.component.ts b/src/main/webapp/app/admin/organization-management/organization-management.component.ts index 9711658af330..c6473eebd30d 100644 --- a/src/main/webapp/app/admin/organization-management/organization-management.component.ts +++ b/src/main/webapp/app/admin/organization-management/organization-management.component.ts @@ -1,15 +1,22 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { Organization } from 'app/entities/organization.model'; import { OrganizationManagementService } from 'app/admin/organization-management/organization-management.service'; import { Subject } from 'rxjs'; import { faEye, faPlus, faTimes, faWrench } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; @Component({ selector: 'jhi-organization-management', templateUrl: './organization-management.component.html', + imports: [TranslateDirective, RouterLink, FaIconComponent, DeleteButtonDirective], }) export class OrganizationManagementComponent implements OnInit { + private organizationService = inject(OrganizationManagementService); + organizations: Organization[]; private dialogErrorSource = new Subject(); @@ -21,8 +28,6 @@ export class OrganizationManagementComponent implements OnInit { faEye = faEye; faWrench = faWrench; - constructor(private organizationService: OrganizationManagementService) {} - ngOnInit(): void { this.organizationService.getOrganizations().subscribe((organizations) => { this.organizations = organizations; diff --git a/src/main/webapp/app/admin/organization-management/organization-management.route.ts b/src/main/webapp/app/admin/organization-management/organization-management.route.ts index 6cedadd30b4c..a2f68870c6fb 100644 --- a/src/main/webapp/app/admin/organization-management/organization-management.route.ts +++ b/src/main/webapp/app/admin/organization-management/organization-management.route.ts @@ -1,13 +1,11 @@ import { Route } from '@angular/router'; -import { OrganizationManagementComponent } from 'app/admin/organization-management/organization-management.component'; -import { OrganizationManagementUpdateComponent } from 'app/admin/organization-management/organization-management-update.component'; -import { OrganizationManagementDetailComponent } from 'app/admin/organization-management/organization-management-detail.component'; + import { OrganizationManagementResolve } from 'app/admin/organization-management/organization-management-resolve.service'; export const organizationMgmtRoute: Route[] = [ { path: 'organization-management', - component: OrganizationManagementComponent, + loadComponent: () => import('app/admin/organization-management/organization-management.component').then((m) => m.OrganizationManagementComponent), data: { pageTitle: 'artemisApp.organizationManagement.title', }, @@ -20,7 +18,7 @@ export const organizationMgmtRoute: Route[] = [ children: [ { path: 'new', - component: OrganizationManagementUpdateComponent, + loadComponent: () => import('app/admin/organization-management/organization-management-update.component').then((m) => m.OrganizationManagementUpdateComponent), resolve: { organization: OrganizationManagementResolve, }, @@ -30,7 +28,7 @@ export const organizationMgmtRoute: Route[] = [ }, { path: ':id', - component: OrganizationManagementDetailComponent, + loadComponent: () => import('app/admin/organization-management/organization-management-detail.component').then((m) => m.OrganizationManagementDetailComponent), resolve: { organization: OrganizationManagementResolve, }, @@ -50,7 +48,8 @@ export const organizationMgmtRoute: Route[] = [ children: [ { path: 'edit', - component: OrganizationManagementUpdateComponent, + loadComponent: () => + import('app/admin/organization-management/organization-management-update.component').then((m) => m.OrganizationManagementUpdateComponent), data: { pageTitle: 'artemisApp.organizationManagement.addOrEditLabel', breadcrumbLabelVariable: 'organization.id', diff --git a/src/main/webapp/app/admin/organization-management/organization-management.service.ts b/src/main/webapp/app/admin/organization-management/organization-management.service.ts index f488abe062b3..7b89d23a5ce1 100644 --- a/src/main/webapp/app/admin/organization-management/organization-management.service.ts +++ b/src/main/webapp/app/admin/organization-management/organization-management.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable, tap } from 'rxjs'; @@ -8,14 +8,12 @@ import { EntityTitleService, EntityType } from 'app/shared/layouts/navbar/entity @Injectable({ providedIn: 'root' }) export class OrganizationManagementService { + private http = inject(HttpClient); + private entityTitleService = inject(EntityTitleService); + public resourceUrl = 'api/organizations'; public adminResourceUrl = 'api/admin/organizations'; - constructor( - private http: HttpClient, - private entityTitleService: EntityTitleService, - ) {} - /** * Send GET request to retrieve all organizations */ diff --git a/src/main/webapp/app/admin/standardized-competencies/import/admin-import-standardized-competencies.component.ts b/src/main/webapp/app/admin/standardized-competencies/import/admin-import-standardized-competencies.component.ts index bd63f1306106..5355c7e853f8 100644 --- a/src/main/webapp/app/admin/standardized-competencies/import/admin-import-standardized-competencies.component.ts +++ b/src/main/webapp/app/admin/standardized-competencies/import/admin-import-standardized-competencies.component.ts @@ -24,6 +24,7 @@ import { ArtemisSharedComponentModule } from 'app/shared/components/shared-compo import { StandardizedCompetencyDetailComponent } from 'app/shared/standardized-competencies/standardized-competency-detail.component'; import { KnowledgeAreaTreeComponent } from 'app/shared/standardized-competencies/knowledge-area-tree.component'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; +import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; interface ImportCount { knowledgeAreas: number; @@ -32,9 +33,16 @@ interface ImportCount { @Component({ selector: 'jhi-admin-import-standardized-competencies', - standalone: true, templateUrl: './admin-import-standardized-competencies.component.html', - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisMarkdownModule, FontAwesomeModule, StandardizedCompetencyDetailComponent, KnowledgeAreaTreeComponent], + imports: [ + ArtemisSharedModule, + ArtemisSharedComponentModule, + ArtemisMarkdownModule, + FontAwesomeModule, + StandardizedCompetencyDetailComponent, + KnowledgeAreaTreeComponent, + NgbCollapse, + ], }) export class AdminImportStandardizedCompetenciesComponent { protected isLoading = false; @@ -60,7 +68,7 @@ export class AdminImportStandardizedCompetenciesComponent { protected readonly importExample = `\`\`\` { "knowledgeAreas": [{ - "title": "Artifical Intelligence", + "title": "Artificial Intelligence", "shortTitle": "AI", "description": "AI is a field in computer science...", //(optional) "competencies": [{ diff --git a/src/main/webapp/app/admin/standardized-competencies/knowledge-area-edit.component.ts b/src/main/webapp/app/admin/standardized-competencies/knowledge-area-edit.component.ts index 935b8ab26609..0ff068bee384 100644 --- a/src/main/webapp/app/admin/standardized-competencies/knowledge-area-edit.component.ts +++ b/src/main/webapp/app/admin/standardized-competencies/knowledge-area-edit.component.ts @@ -1,15 +1,24 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { faBan, faPencil, faPlus, faSave, faTrash } from '@fortawesome/free-solid-svg-icons'; import { KnowledgeArea, KnowledgeAreaDTO, KnowledgeAreaValidators } from 'app/entities/competency/standardized-competency.model'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { Observable } from 'rxjs'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-knowledge-area-edit', templateUrl: './knowledge-area-edit.component.html', + imports: [TranslateDirective, ButtonComponent, DeleteButtonDirective, FaIconComponent, FormsModule, ReactiveFormsModule, MarkdownEditorMonacoComponent, HtmlForMarkdownPipe], }) export class KnowledgeAreaEditComponent { + private formBuilder = inject(FormBuilder); + // values for the knowledge area select @Input() knowledgeAreas: KnowledgeArea[] = []; @Input({ required: true }) set knowledgeArea(knowledgeArea: KnowledgeAreaDTO) { @@ -72,8 +81,6 @@ export class KnowledgeAreaEditComponent { protected readonly ButtonType = ButtonType; protected readonly validators = KnowledgeAreaValidators; - constructor(private formBuilder: FormBuilder) {} - save() { const updatedValues = this.form.getRawValue(); const updatedKnowledgeArea: KnowledgeAreaDTO = { ...this.knowledgeArea, ...updatedValues }; @@ -128,7 +135,7 @@ export class KnowledgeAreaEditComponent { // if the knowledgeArea is new, no validator is needed. if (this.knowledgeArea.id === undefined) { // eslint-disable-next-line @typescript-eslint/no-unused-vars - return (parentIdControl: FormControl) => null; + return (_parentIdControl: FormControl) => null; } return (parentIdControl: FormControl) => { if (parentIdControl.value === undefined) { diff --git a/src/main/webapp/app/admin/standardized-competencies/standardized-competency-edit.component.ts b/src/main/webapp/app/admin/standardized-competencies/standardized-competency-edit.component.ts index 8644cbaacffd..d7fdab0f675b 100644 --- a/src/main/webapp/app/admin/standardized-competencies/standardized-competency-edit.component.ts +++ b/src/main/webapp/app/admin/standardized-competencies/standardized-competency-edit.component.ts @@ -1,16 +1,36 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { faBan, faPencil, faSave, faTrash } from '@fortawesome/free-solid-svg-icons'; import { KnowledgeArea, Source, StandardizedCompetencyDTO, StandardizedCompetencyValidators } from 'app/entities/competency/standardized-competency.model'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { CompetencyTaxonomy } from 'app/entities/competency.model'; import { Observable } from 'rxjs'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { TaxonomySelectComponent } from 'app/course/competencies/taxonomy-select/taxonomy-select.component'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-standardized-competency-edit', templateUrl: './standardized-competency-edit.component.html', + imports: [ + TranslateDirective, + ButtonComponent, + DeleteButtonDirective, + FaIconComponent, + FormsModule, + ReactiveFormsModule, + MarkdownEditorMonacoComponent, + TaxonomySelectComponent, + HtmlForMarkdownPipe, + ], }) export class StandardizedCompetencyEditComponent { + private formBuilder = inject(FormBuilder); + // values for the knowledge area select @Input() knowledgeAreas: KnowledgeArea[] = []; // values for the source select @@ -74,8 +94,6 @@ export class StandardizedCompetencyEditComponent { protected readonly ButtonType = ButtonType; protected readonly validators = StandardizedCompetencyValidators; - constructor(private formBuilder: FormBuilder) {} - save() { const updatedValues = this.form.getRawValue(); const updatedCompetency: StandardizedCompetencyDTO = { ...this.competency, ...updatedValues }; diff --git a/src/main/webapp/app/admin/standardized-competencies/standardized-competency-management.component.ts b/src/main/webapp/app/admin/standardized-competencies/standardized-competency-management.component.ts index d23fe7213f6d..1d98957a1225 100644 --- a/src/main/webapp/app/admin/standardized-competencies/standardized-competency-management.component.ts +++ b/src/main/webapp/app/admin/standardized-competencies/standardized-competency-management.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { faChevronRight, faDownLeftAndUpRightToCenter, faEye, faFileExport, faFileImport, faPlus, faUpRightAndDownLeftFromCenter } from '@fortawesome/free-solid-svg-icons'; import { KnowledgeAreaDTO, @@ -13,7 +13,7 @@ import { AdminStandardizedCompetencyService } from 'app/admin/standardized-compe import { HttpErrorResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; import { Subject, forkJoin, map } from 'rxjs'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { ConfirmAutofocusModalComponent } from 'app/shared/components/confirm-autofocus-modal.component'; import { getIcon } from 'app/entities/competency.model'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; @@ -22,14 +22,44 @@ import { StandardizedCompetencyFilterPageComponent } from 'app/shared/standardiz import { ComponentCanDeactivate } from 'app/shared/guard/can-deactivate.model'; import { StandardizedCompetencyService } from 'app/shared/standardized-competencies/standardized-competency.service'; import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { StandardizedCompetencyFilterComponent } from 'app/shared/standardized-competencies/standardized-competency-filter.component'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { KnowledgeAreaTreeComponent } from 'app/shared/standardized-competencies/knowledge-area-tree.component'; +import { StandardizedCompetencyEditComponent } from './standardized-competency-edit.component'; +import { KnowledgeAreaEditComponent } from './knowledge-area-edit.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-standardized-competency-management', templateUrl: './standardized-competency-management.component.html', styleUrls: ['standardized-competency-management.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + TranslateDirective, + DocumentationButtonComponent, + RouterLink, + FaIconComponent, + StandardizedCompetencyFilterComponent, + ButtonComponent, + KnowledgeAreaTreeComponent, + NgbTooltip, + StandardizedCompetencyEditComponent, + KnowledgeAreaEditComponent, + ArtemisTranslatePipe, + ], }) export class StandardizedCompetencyManagementComponent extends StandardizedCompetencyFilterPageComponent implements OnInit, OnDestroy, ComponentCanDeactivate { + private adminStandardizedCompetencyService = inject(AdminStandardizedCompetencyService); + private standardizedCompetencyService = inject(StandardizedCompetencyService); + private alertService = inject(AlertService); + private modalService = inject(NgbModal); + private translateService = inject(TranslateService); + private changeDetectorRef = inject(ChangeDetectorRef); + protected isLoading = false; // true if a competency is getting edited in the detail component protected isEditing = false; @@ -58,17 +88,6 @@ export class StandardizedCompetencyManagementComponent extends StandardizedCompe protected readonly getIcon = getIcon; readonly documentationType: DocumentationType = 'StandardizedCompetencies'; - constructor( - private adminStandardizedCompetencyService: AdminStandardizedCompetencyService, - private standardizedCompetencyService: StandardizedCompetencyService, - private alertService: AlertService, - private modalService: NgbModal, - private translateService: TranslateService, - private changeDetectorRef: ChangeDetectorRef, - ) { - super(); - } - ngOnInit() { this.isLoading = true; const getKnowledgeAreasObservable = this.standardizedCompetencyService.getAllForTreeView(); diff --git a/src/main/webapp/app/admin/statistics/statistics.component.ts b/src/main/webapp/app/admin/statistics/statistics.component.ts index ab7543bc028c..b9a3c2d0962f 100644 --- a/src/main/webapp/app/admin/statistics/statistics.component.ts +++ b/src/main/webapp/app/admin/statistics/statistics.component.ts @@ -1,9 +1,13 @@ import { Component } from '@angular/core'; import { Graphs, SpanType, StatisticsView } from 'app/entities/statistics.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { StatisticsGraphComponent } from 'app/shared/statistics-graph/statistics-graph.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-statistics', templateUrl: './statistics.component.html', + imports: [TranslateDirective, StatisticsGraphComponent, ArtemisTranslatePipe], }) export class StatisticsComponent { // html properties @@ -24,8 +28,6 @@ export class StatisticsComponent { currentSpan: SpanType = SpanType.WEEK; statisticsView: StatisticsView = StatisticsView.ARTEMIS; - constructor() {} - onTabChanged(span: SpanType): void { this.currentSpan = span; } diff --git a/src/main/webapp/app/admin/system-notification-management/system-notification-management-detail.component.ts b/src/main/webapp/app/admin/system-notification-management/system-notification-management-detail.component.ts index 05c64dec18e7..9aa7d3a66f4c 100644 --- a/src/main/webapp/app/admin/system-notification-management/system-notification-management-detail.component.ts +++ b/src/main/webapp/app/admin/system-notification-management/system-notification-management-detail.component.ts @@ -1,24 +1,26 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { Component, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, Router, RouterLink, RouterOutlet } from '@angular/router'; import { faWrench } from '@fortawesome/free-solid-svg-icons'; import { SystemNotification } from 'app/entities/system-notification.model'; import { SystemNotificationService } from 'app/shared/notification/system-notification/system-notification.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-system-notification-management-detail', templateUrl: './system-notification-management-detail.component.html', + imports: [TranslateDirective, RouterLink, FaIconComponent, RouterOutlet, ArtemisDatePipe], }) export class SystemNotificationManagementDetailComponent implements OnInit { + private systemNotificationService = inject(SystemNotificationService); + private route = inject(ActivatedRoute); + private router = inject(Router); + notification: SystemNotification; // Icons faWrench = faWrench; - constructor( - private systemNotificationService: SystemNotificationService, - private route: ActivatedRoute, - private router: Router, - ) {} - /** * Assigns the subscription to system notification service */ diff --git a/src/main/webapp/app/admin/system-notification-management/system-notification-management-resolve.service.ts b/src/main/webapp/app/admin/system-notification-management/system-notification-management-resolve.service.ts index e0741f531f38..37ed2da319b8 100644 --- a/src/main/webapp/app/admin/system-notification-management/system-notification-management-resolve.service.ts +++ b/src/main/webapp/app/admin/system-notification-management/system-notification-management-resolve.service.ts @@ -1,5 +1,5 @@ import { HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; import { SystemNotification } from 'app/entities/system-notification.model'; import { SystemNotificationService } from 'app/shared/notification/system-notification/system-notification.service'; @@ -7,7 +7,7 @@ import { filter, map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class SystemNotificationManagementResolve implements Resolve { - constructor(private service: SystemNotificationService) {} + private service = inject(SystemNotificationService); /** * Resolves the route and initializes system notification from id route param diff --git a/src/main/webapp/app/admin/system-notification-management/system-notification-management-update.component.ts b/src/main/webapp/app/admin/system-notification-management/system-notification-management-update.component.ts index 75523020fa0d..18ca17c3372c 100644 --- a/src/main/webapp/app/admin/system-notification-management/system-notification-management-update.component.ts +++ b/src/main/webapp/app/admin/system-notification-management/system-notification-management-update.component.ts @@ -1,17 +1,27 @@ -import { Component, OnInit } from '@angular/core'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { Component, OnInit, inject } from '@angular/core'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { faBan, faSave } from '@fortawesome/free-solid-svg-icons'; import { UserService } from 'app/core/user/user.service'; import { SystemNotification, SystemNotificationType } from 'app/entities/system-notification.model'; import dayjs from 'dayjs/esm'; import { AdminSystemNotificationService } from 'app/shared/notification/system-notification/admin-system-notification.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-system-notification-management-update', templateUrl: './system-notification-management-update.component.html', + imports: [FormsModule, ReactiveFormsModule, TranslateDirective, FormDateTimePickerComponent, FaIconComponent, ArtemisTranslatePipe], }) export class SystemNotificationManagementUpdateComponent implements OnInit { + private userService = inject(UserService); + private systemNotificationService = inject(AdminSystemNotificationService); + private route = inject(ActivatedRoute); + private router = inject(Router); + notification: SystemNotification; isSaving: boolean; @@ -26,13 +36,6 @@ export class SystemNotificationManagementUpdateComponent implements OnInit { faSave = faSave; faBan = faBan; - constructor( - private userService: UserService, - private systemNotificationService: AdminSystemNotificationService, - private route: ActivatedRoute, - private router: Router, - ) {} - /** * Loads notification from route data */ diff --git a/src/main/webapp/app/admin/system-notification-management/system-notification-management.component.ts b/src/main/webapp/app/admin/system-notification-management/system-notification-management.component.ts index 89d80d04b471..d6e44743fe9b 100644 --- a/src/main/webapp/app/admin/system-notification-management/system-notification-management.component.ts +++ b/src/main/webapp/app/admin/system-notification-management/system-notification-management.component.ts @@ -1,6 +1,6 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { HttpErrorResponse, HttpHeaders, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { Subject, Subscription } from 'rxjs'; import { User } from 'app/core/user/user.model'; import { AccountService } from 'app/core/auth/account.service'; @@ -14,6 +14,14 @@ import { EventManager } from 'app/core/util/event-manager.service'; import { ParseLinks } from 'app/core/util/parse-links.service'; import { faEye, faPlus, faSort, faTimes, faWrench } from '@fortawesome/free-solid-svg-icons'; import { AdminSystemNotificationService } from 'app/shared/notification/system-notification/admin-system-notification.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ItemCountComponent } from 'app/shared/pagination/item-count.component'; +import { NgbPagination } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; enum NotificationState { SCHEDULED = 'SCHEDULED', @@ -24,8 +32,18 @@ enum NotificationState { @Component({ selector: 'jhi-system-notification-management', templateUrl: './system-notification-management.component.html', + imports: [TranslateDirective, RouterLink, FaIconComponent, SortDirective, SortByDirective, DeleteButtonDirective, ItemCountComponent, NgbPagination, ArtemisDatePipe], }) export class SystemNotificationManagementComponent implements OnInit, OnDestroy { + private systemNotificationService = inject(SystemNotificationService); + private adminSystemNotificationService = inject(AdminSystemNotificationService); + private alertService = inject(AlertService); + private accountService = inject(AccountService); + private parseLinks = inject(ParseLinks); + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private eventManager = inject(EventManager); + readonly SCHEDULED = NotificationState.SCHEDULED; readonly ACTIVE = NotificationState.ACTIVE; readonly EXPIRED = NotificationState.EXPIRED; @@ -55,16 +73,7 @@ export class SystemNotificationManagementComponent implements OnInit, OnDestroy faEye = faEye; faWrench = faWrench; - constructor( - private systemNotificationService: SystemNotificationService, - private adminSystemNotificationService: AdminSystemNotificationService, - private alertService: AlertService, - private accountService: AccountService, - private parseLinks: ParseLinks, - private activatedRoute: ActivatedRoute, - private router: Router, - private eventManager: EventManager, - ) { + constructor() { this.routeData = this.activatedRoute.data.subscribe((data) => { const pagingParams = data['pagingParams']; if (pagingParams) { diff --git a/src/main/webapp/app/admin/system-notification-management/system-notification-management.route.ts b/src/main/webapp/app/admin/system-notification-management/system-notification-management.route.ts index 7a5b01f2d107..09ff2defee8a 100644 --- a/src/main/webapp/app/admin/system-notification-management/system-notification-management.route.ts +++ b/src/main/webapp/app/admin/system-notification-management/system-notification-management.route.ts @@ -1,13 +1,11 @@ import { Route } from '@angular/router'; -import { SystemNotificationManagementUpdateComponent } from 'app/admin/system-notification-management/system-notification-management-update.component'; -import { SystemNotificationManagementComponent } from 'app/admin/system-notification-management/system-notification-management.component'; -import { SystemNotificationManagementDetailComponent } from 'app/admin/system-notification-management/system-notification-management-detail.component'; + import { SystemNotificationManagementResolve } from 'app/admin/system-notification-management/system-notification-management-resolve.service'; export const systemNotificationManagementRoute: Route[] = [ { path: 'system-notification-management', - component: SystemNotificationManagementComponent, + loadComponent: () => import('app/admin/system-notification-management/system-notification-management.component').then((m) => m.SystemNotificationManagementComponent), data: { pageTitle: 'artemisApp.systemNotification.systemNotifications', defaultSort: 'id,asc', @@ -22,14 +20,16 @@ export const systemNotificationManagementRoute: Route[] = [ children: [ { path: 'new', - component: SystemNotificationManagementUpdateComponent, + loadComponent: () => + import('app/admin/system-notification-management/system-notification-management-update.component').then((m) => m.SystemNotificationManagementUpdateComponent), data: { pageTitle: 'global.generic.create', }, }, { path: ':id', - component: SystemNotificationManagementDetailComponent, + loadComponent: () => + import('app/admin/system-notification-management/system-notification-management-detail.component').then((m) => m.SystemNotificationManagementDetailComponent), resolve: { notification: SystemNotificationManagementResolve, }, @@ -50,7 +50,10 @@ export const systemNotificationManagementRoute: Route[] = [ children: [ { path: 'edit', - component: SystemNotificationManagementUpdateComponent, + loadComponent: () => + import('app/admin/system-notification-management/system-notification-management-update.component').then( + (m) => m.SystemNotificationManagementUpdateComponent, + ), data: { pageTitle: 'global.generic.edit', breadcrumbLabelVariable: '', diff --git a/src/main/webapp/app/admin/upcoming-exams-and-exercises/upcoming-exams-and-exercises.component.ts b/src/main/webapp/app/admin/upcoming-exams-and-exercises/upcoming-exams-and-exercises.component.ts index 8855ae1fb047..d1b8ee625bb0 100644 --- a/src/main/webapp/app/admin/upcoming-exams-and-exercises/upcoming-exams-and-exercises.component.ts +++ b/src/main/webapp/app/admin/upcoming-exams-and-exercises/upcoming-exams-and-exercises.component.ts @@ -1,29 +1,31 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; import { EntityArrayResponseType as ExerciseEntityArrayResponseType, ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { Exercise } from 'app/entities/exercise.model'; import { SortService } from 'app/shared/service/sort.service'; import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { RouterLink } from '@angular/router'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-upcoming-exams-and-exercises', templateUrl: './upcoming-exams-and-exercises.component.html', styles: ['.table {table-layout: fixed}'], + imports: [TranslateDirective, RouterLink, ArtemisDatePipe], }) export class UpcomingExamsAndExercisesComponent implements OnInit { + private exerciseService = inject(ExerciseService); + private examManagementService = inject(ExamManagementService); + private sortService = inject(SortService); + upcomingExercises: Exercise[] = []; upcomingExams: Exam[] = []; predicate: string; reverse: boolean; - constructor( - private exerciseService: ExerciseService, - private examManagementService: ExamManagementService, - private sortService: SortService, - ) {} - ngOnInit(): void { this.exerciseService.getUpcomingExercises().subscribe((res: ExerciseEntityArrayResponseType) => { this.upcomingExercises = res.body ?? []; diff --git a/src/main/webapp/app/admin/user-management/delete-users-button.component.ts b/src/main/webapp/app/admin/user-management/delete-users-button.component.ts index 1a4bfdd17927..1ef962c85b3d 100644 --- a/src/main/webapp/app/admin/user-management/delete-users-button.component.ts +++ b/src/main/webapp/app/admin/user-management/delete-users-button.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Output, WritableSignal, signal } from '@angular/core'; +import { Component, EventEmitter, Output, WritableSignal, inject, signal } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { faEraser } from '@fortawesome/free-solid-svg-icons'; import { ArtemisSharedModule } from 'app/shared/shared.module'; @@ -16,12 +16,15 @@ import { ArtemisSharedComponentModule } from 'app/shared/components/shared-compo * that shows a list of the logins of the users which will be deleted. */ @Component({ - standalone: true, selector: 'jhi-delete-users-button', templateUrl: './delete-users-button.component.html', imports: [ArtemisSharedModule, ArtemisSharedComponentModule], }) export class DeleteUsersButtonComponent { + private adminUserService = inject(AdminUserService); + private alertService = inject(AlertService); + private deleteDialogService = inject(DeleteDialogService); + @Output() deletionCompleted = new EventEmitter<{ [key: string]: boolean }>(); users: WritableSignal = signal(undefined); @@ -33,12 +36,6 @@ export class DeleteUsersButtonComponent { faEraser = faEraser; protected readonly ButtonType = ButtonType; - constructor( - private adminUserService: AdminUserService, - private alertService: AlertService, - private deleteDialogService: DeleteDialogService, - ) {} - /** * Load the list of users to user confirmation and delete. */ diff --git a/src/main/webapp/app/admin/user-management/user-management-detail.component.ts b/src/main/webapp/app/admin/user-management/user-management-detail.component.ts index f1393613b9aa..c00a1b42f177 100644 --- a/src/main/webapp/app/admin/user-management/user-management-detail.component.ts +++ b/src/main/webapp/app/admin/user-management/user-management-detail.component.ts @@ -1,19 +1,24 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink, RouterOutlet } from '@angular/router'; import { faWrench } from '@fortawesome/free-solid-svg-icons'; import { User } from 'app/core/user/user.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-user-management-detail', templateUrl: './user-management-detail.component.html', + imports: [TranslateDirective, RouterLink, FaIconComponent, RouterOutlet, ArtemisDatePipe, ArtemisTranslatePipe], }) export class UserManagementDetailComponent implements OnInit { + private route = inject(ActivatedRoute); + user: User; // Icons faWrench = faWrench; - constructor(private route: ActivatedRoute) {} - /** * Retrieve the user from the user management activated route data subscription * and get the user based on the login string diff --git a/src/main/webapp/app/admin/user-management/user-management-resolve.service.ts b/src/main/webapp/app/admin/user-management/user-management-resolve.service.ts index 41e76ccc9a75..a0e4dd9396e2 100644 --- a/src/main/webapp/app/admin/user-management/user-management-resolve.service.ts +++ b/src/main/webapp/app/admin/user-management/user-management-resolve.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; import { User } from 'app/core/user/user.model'; import { Observable, of } from 'rxjs'; @@ -6,7 +6,7 @@ import { AdminUserService } from 'app/core/user/admin-user.service'; @Injectable({ providedIn: 'root' }) export class UserManagementResolve implements Resolve { - constructor(private adminUserService: AdminUserService) {} + private adminUserService = inject(AdminUserService); /** * Resolve route to find the user before the route is activated diff --git a/src/main/webapp/app/admin/user-management/user-management-update.component.ts b/src/main/webapp/app/admin/user-management/user-management-update.component.ts index 2d7e318f9038..3ccd94670076 100644 --- a/src/main/webapp/app/admin/user-management/user-management-update.component.ts +++ b/src/main/webapp/app/admin/user-management/user-management-update.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { User } from 'app/core/user/user.model'; import { JhiLanguageHelper } from 'app/core/language/language.helper'; @@ -6,26 +6,64 @@ import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; import { OrganizationManagementService } from 'app/admin/organization-management/organization-management.service'; import { OrganizationSelectorComponent } from 'app/shared/organization-selector/organization-selector.component'; import { Organization } from 'app/entities/organization.model'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, USERNAME_MAX_LENGTH, USERNAME_MIN_LENGTH } from 'app/app.constants'; import { faBan, faSave, faTimes } from '@fortawesome/free-solid-svg-icons'; import { COMMA, ENTER, TAB } from '@angular/cdk/keycodes'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; -import { MatChipInputEvent } from '@angular/material/chips'; -import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; +import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { MatChipGrid, MatChipInput, MatChipInputEvent, MatChipRemove, MatChipRow } from '@angular/material/chips'; +import { MatAutocomplete, MatAutocompleteSelectedEvent, MatAutocompleteTrigger } from '@angular/material/autocomplete'; import { AlertService, AlertType } from 'app/core/util/alert.service'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { AdminUserService } from 'app/core/user/admin-user.service'; import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; import { CourseAdminService } from 'app/course/manage/course-admin.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { MatFormField } from '@angular/material/form-field'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { MatOption } from '@angular/material/core'; +import { AsyncPipe } from '@angular/common'; +import { FindLanguageFromKeyPipe } from 'app/shared/language/find-language-from-key.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-user-management-update', templateUrl: './user-management-update.component.html', styleUrls: ['./user-management-update.component.scss'], + imports: [ + FormsModule, + ReactiveFormsModule, + TranslateDirective, + NgbTooltip, + HelpIconComponent, + MatFormField, + MatChipGrid, + MatChipRow, + MatChipRemove, + FaIconComponent, + MatAutocompleteTrigger, + MatChipInput, + MatAutocomplete, + MatOption, + AsyncPipe, + FindLanguageFromKeyPipe, + ArtemisTranslatePipe, + ], }) export class UserManagementUpdateComponent implements OnInit { + private languageHelper = inject(JhiLanguageHelper); + private userService = inject(AdminUserService); + private courseAdminService = inject(CourseAdminService); + private route = inject(ActivatedRoute); + private organizationService = inject(OrganizationManagementService); + private modalService = inject(NgbModal); + private navigationUtilService = inject(ArtemisNavigationUtilService); + private alertService = inject(AlertService); + private profileService = inject(ProfileService); + private fb = inject(FormBuilder); + readonly USERNAME_MIN_LENGTH = USERNAME_MIN_LENGTH; readonly USERNAME_MAX_LENGTH = USERNAME_MAX_LENGTH; readonly PASSWORD_MIN_LENGTH = PASSWORD_MIN_LENGTH; @@ -55,19 +93,6 @@ export class UserManagementUpdateComponent implements OnInit { private oldLogin?: string; private isJenkins: boolean; - constructor( - private languageHelper: JhiLanguageHelper, - private userService: AdminUserService, - private courseAdminService: CourseAdminService, - private route: ActivatedRoute, - private organizationService: OrganizationManagementService, - private modalService: NgbModal, - private navigationUtilService: ArtemisNavigationUtilService, - private alertService: AlertService, - private profileService: ProfileService, - private fb: FormBuilder, - ) {} - /** * Enable subscriptions to retrieve the user based on the activated route, all authorities and all languages on init */ diff --git a/src/main/webapp/app/admin/user-management/user-management.component.ts b/src/main/webapp/app/admin/user-management/user-management.component.ts index f7870f5369cf..56e4c06bf76f 100644 --- a/src/main/webapp/app/admin/user-management/user-management.component.ts +++ b/src/main/webapp/app/admin/user-management/user-management.component.ts @@ -1,6 +1,6 @@ -import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, OnDestroy, OnInit, TemplateRef, ViewChild, inject } from '@angular/core'; import { HttpErrorResponse, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { Subject, Subscription, combineLatest } from 'rxjs'; import { onError } from 'app/shared/util/global.utils'; import { User } from 'app/core/user/user.model'; @@ -8,15 +8,28 @@ import { AccountService } from 'app/core/auth/account.service'; import { AlertService } from 'app/core/util/alert.service'; import { SortingOrder } from 'app/shared/table/pageable-table'; import { switchMap, tap } from 'rxjs/operators'; -import { FormControl, FormGroup } from '@angular/forms'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { EventManager } from 'app/core/util/event-manager.service'; import { ASC, DESC, ITEMS_PER_PAGE, SORT } from 'app/shared/constants/pagination.constants'; import { faEye, faFilter, faPlus, faSort, faTimes, faWrench } from '@fortawesome/free-solid-svg-icons'; import { LocalStorageService } from 'ngx-webstorage'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbHighlight, NgbModal, NgbPagination } from '@ng-bootstrap/ng-bootstrap'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { AdminUserService } from 'app/core/user/admin-user.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { UsersImportButtonComponent } from 'app/shared/user-import/users-import-button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { DeleteUsersButtonComponent } from './delete-users-button.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { NgClass } from '@angular/common'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; +import { ItemCountComponent } from 'app/shared/pagination/item-count.component'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export class UserFilter { authorityFilter: Set = new Set(); @@ -86,8 +99,38 @@ type Filter = typeof AuthorityFilter | typeof OriginFilter | typeof StatusFilter selector: 'jhi-user-management', templateUrl: './user-management.component.html', styleUrls: ['./user-management.component.scss'], + imports: [ + TranslateDirective, + UsersImportButtonComponent, + RouterLink, + FaIconComponent, + FormsModule, + ReactiveFormsModule, + DeleteUsersButtonComponent, + DeleteButtonDirective, + NgClass, + SortDirective, + SortByDirective, + ProfilePictureComponent, + NgbHighlight, + ItemCountComponent, + NgbPagination, + HelpIconComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + ], }) export class UserManagementComponent implements OnInit, OnDestroy { + private adminUserService = inject(AdminUserService); + private alertService = inject(AlertService); + private accountService = inject(AccountService); + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private eventManager = inject(EventManager); + private localStorage = inject(LocalStorageService); + private modalService = inject(NgbModal); + private profileService = inject(ProfileService); + @ViewChild('filterModal') filterModal: TemplateRef; search = new Subject(); @@ -127,18 +170,6 @@ export class UserManagementComponent implements OnInit, OnDestroy { readonly medium = ButtonSize.MEDIUM; readonly ButtonType = ButtonType; - constructor( - private adminUserService: AdminUserService, - private alertService: AlertService, - private accountService: AccountService, - private activatedRoute: ActivatedRoute, - private router: Router, - private eventManager: EventManager, - private localStorage: LocalStorageService, - private modalService: NgbModal, - private profileService: ProfileService, - ) {} - /** * Retrieves the current user and calls the {@link loadAll} and {@link registerChangeInUsers} methods on init */ diff --git a/src/main/webapp/app/admin/user-management/user-management.route.ts b/src/main/webapp/app/admin/user-management/user-management.route.ts index df2178c4a68b..8c0bec4dd6c6 100644 --- a/src/main/webapp/app/admin/user-management/user-management.route.ts +++ b/src/main/webapp/app/admin/user-management/user-management.route.ts @@ -1,13 +1,11 @@ import { Route } from '@angular/router'; -import { UserManagementDetailComponent } from 'app/admin/user-management/user-management-detail.component'; -import { UserManagementComponent } from 'app/admin/user-management/user-management.component'; -import { UserManagementUpdateComponent } from 'app/admin/user-management/user-management-update.component'; + import { UserManagementResolve } from 'app/admin/user-management/user-management-resolve.service'; export const userManagementRoute: Route[] = [ { path: 'user-management', - component: UserManagementComponent, + loadComponent: () => import('app/admin/user-management/user-management.component').then((m) => m.UserManagementComponent), data: { pageTitle: 'artemisApp.userManagement.home.title', defaultSort: 'id,asc', @@ -22,7 +20,7 @@ export const userManagementRoute: Route[] = [ children: [ { path: 'new', - component: UserManagementUpdateComponent, + loadComponent: () => import('app/admin/user-management/user-management-update.component').then((m) => m.UserManagementUpdateComponent), resolve: { user: UserManagementResolve, }, @@ -32,7 +30,7 @@ export const userManagementRoute: Route[] = [ }, { path: ':login', - component: UserManagementDetailComponent, + loadComponent: () => import('app/admin/user-management/user-management-detail.component').then((m) => m.UserManagementDetailComponent), resolve: { user: UserManagementResolve, }, @@ -49,7 +47,7 @@ export const userManagementRoute: Route[] = [ children: [ { path: 'edit', - component: UserManagementUpdateComponent, + loadComponent: () => import('app/admin/user-management/user-management-update.component').then((m) => m.UserManagementUpdateComponent), data: { pageTitle: 'artemisApp.userManagement.home.editLabel', }, diff --git a/src/main/webapp/app/app-routing.module.ts b/src/main/webapp/app/app-routing.module.ts deleted file mode 100644 index 607fb3a3f6b8..000000000000 --- a/src/main/webapp/app/app-routing.module.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { Authority } from 'app/shared/constants/authority.constants'; -import { navbarRoute } from 'app/shared/layouts/navbar/navbar.route'; -import { errorRoute } from 'app/shared/layouts/error/error.route'; -import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; - -const LAYOUT_ROUTES: Routes = [navbarRoute, ...errorRoute]; - -@NgModule({ - imports: [ - RouterModule.forRoot( - [ - ...LAYOUT_ROUTES, - { - path: '', - loadComponent: () => import('./home/home.component').then((m) => m.HomeComponent), - data: { - pageTitle: 'home.title', - }, - }, - { - path: 'admin', - loadChildren: () => import('./admin/admin.module').then((m) => m.ArtemisAdminModule), - }, - { - path: 'privacy', - loadChildren: () => import('./core/legal/privacy.module').then((m) => m.ArtemisPrivacyModule), - }, - { - path: 'imprint', - loadChildren: () => import('./core/legal/imprint.module').then((m) => m.ArtemisImprintModule), - }, - { - path: 'about', - loadChildren: () => import('./core/about-us/artemis-about-us.module').then((module) => module.ArtemisAboutUsModule), - }, - // ===== TEAM ==== - { - path: 'course-management/:courseId/exercises/:exerciseId/teams', - loadChildren: () => import('./exercises/shared/team/team.module').then((m) => m.ArtemisTeamModule), - }, - { - path: 'courses/:courseId/exercises/:exerciseId/teams', - loadChildren: () => import('./exercises/shared/team/team.module').then((m) => m.ArtemisTeamModule), - }, - // ===== ACCOUNT ==== - { - path: 'account', - children: [ - { - path: 'activate', - pathMatch: 'full', - loadComponent: () => import('./account/activate/activate.component').then((m) => m.ActivateComponent), - data: { - pageTitle: 'activate.title', - }, - }, - { - path: 'password', - pathMatch: 'full', - loadComponent: () => import('./account/password/password.component').then((m) => m.PasswordComponent), - data: { - authorities: [Authority.USER], - pageTitle: 'global.menu.account.password', - }, - canActivate: [UserRouteAccessService], - }, - { - path: 'reset/finish', - pathMatch: 'full', - loadComponent: () => import('./account/password-reset/finish/password-reset-finish.component').then((m) => m.PasswordResetFinishComponent), - data: { - pageTitle: 'global.menu.account.password', - }, - }, - { - path: 'reset/request', - pathMatch: 'full', - loadComponent: () => import('./account/password-reset/init/password-reset-init.component').then((m) => m.PasswordResetInitComponent), - data: { - pageTitle: 'global.menu.account.password', - }, - }, - { - path: 'register', - pathMatch: 'full', - loadComponent: () => import('./account/register/register.component').then((m) => m.RegisterComponent), - data: { - pageTitle: 'register.title', - }, - }, - { - path: 'settings', - pathMatch: 'full', - loadComponent: () => import('./account/settings/settings.component').then((m) => m.SettingsComponent), - data: { - authorities: [Authority.USER], - pageTitle: 'global.menu.account.settings', - }, - canActivate: [UserRouteAccessService], - }, - ], - }, - // ===== COURSE MANAGEMENT ===== - { - path: 'course-management', - loadChildren: () => import('./course/manage/course-management.module').then((m) => m.ArtemisCourseManagementModule), - }, - { - path: 'course-management/:courseId/programming-exercises/:exerciseId/code-editor', - loadChildren: () => import('./exercises/programming/manage/code-editor/code-editor-management.module').then((m) => m.ArtemisCodeEditorManagementModule), - }, - { - path: 'course-management/:courseId/text-exercises/:exerciseId', - loadChildren: () => import('./exercises/text/assess/text-submission-assessment.module').then((m) => m.ArtemisTextSubmissionAssessmentModule), - }, - { - path: 'course-management/:courseId/text-exercises/:exerciseId/example-submissions/:exampleSubmissionId', - loadChildren: () => import('./exercises/text/manage/example-text-submission/example-text-submission.module').then((m) => m.ArtemisExampleTextSubmissionModule), - }, - { - path: 'course-management/:courseId/programming-exercises/:exerciseId', - loadChildren: () => - import('./exercises/programming/manage/programming-exercise-management-routing.module').then((m) => m.ArtemisProgrammingExerciseManagementRoutingModule), - }, - { - path: 'courses', - loadChildren: () => import('./overview/courses.module').then((m) => m.ArtemisCoursesModule), - }, - { - path: 'course-management/:courseId/lectures/:lectureId/attachments/:attachmentId', - pathMatch: 'full', - loadComponent: () => import('./lecture/pdf-preview/pdf-preview.component').then((m) => m.PdfPreviewComponent), - }, - // ===== GRADING SYSTEM ===== - { - path: 'courses/:courseId/grading-system', - loadChildren: () => import('./grading-system/grading-system.module').then((m) => m.GradingSystemModule), - }, - - { - path: 'courses/:courseId/exercises/:exerciseId/problem-statement', - pathMatch: 'full', - loadComponent: () => import('./overview/exercise-details/problem-statement/problem-statement.component').then((m) => m.ProblemStatementComponent), - }, - { - pathMatch: 'full', - path: 'courses/:courseId/exercises/:exerciseId/problem-statement/:participationId', - loadComponent: () => import('./overview/exercise-details/problem-statement/problem-statement.component').then((m) => m.ProblemStatementComponent), - }, - { - path: 'courses/:courseId/exercises/:exerciseId/participations/:participationId/results/:resultId/feedback', - pathMatch: 'full', - loadComponent: () => import('./exercises/shared/feedback/standalone-feedback/standalone-feedback.component').then((m) => m.StandaloneFeedbackComponent), - }, - - // ===== EXAM ===== - { - path: 'course-management/:courseId/exams', - loadChildren: () => import('./exam/manage/exam-management.module').then((m) => m.ArtemisExamManagementModule), - }, - { - path: 'courses/:courseId/exams/:examId/grading-system', - loadChildren: () => import('./grading-system/grading-system.module').then((m) => m.GradingSystemModule), - }, - { - path: 'courses/:courseId/exams/:examId/exercises/:exerciseId/repository', - loadChildren: () => - import('./exercises/programming/participate/programming-repository-routing.module').then((m) => m.ArtemisProgrammingRepositoryRoutingModule), - }, - { - path: 'features', - loadChildren: () => import('./feature-overview/feature-overview.module').then((m) => m.FeatureOverviewModule), - }, - { - path: 'lti', - loadChildren: () => import('./lti/lti.module').then((m) => m.ArtemisLtiModule), - }, - { - path: 'about-iris', - pathMatch: 'full', - loadComponent: () => import('./iris/about-iris/about-iris.component').then((m) => m.AboutIrisComponent), - data: { - pageTitle: 'artemisApp.exerciseChatbot.title', - }, - }, - ], - { enableTracing: false, onSameUrlNavigation: 'reload' }, - ), - ], - exports: [RouterModule], -}) -export class ArtemisAppRoutingModule { - // Ensure the service is initialized before any routing happens - constructor(private _: ArtemisNavigationUtilService) {} -} diff --git a/src/main/webapp/app/shared/layouts/main/main.component.html b/src/main/webapp/app/app.component.html similarity index 100% rename from src/main/webapp/app/shared/layouts/main/main.component.html rename to src/main/webapp/app/app.component.html diff --git a/src/main/webapp/app/shared/layouts/main/main.component.scss b/src/main/webapp/app/app.component.scss similarity index 100% rename from src/main/webapp/app/shared/layouts/main/main.component.scss rename to src/main/webapp/app/app.component.scss diff --git a/src/main/webapp/app/shared/layouts/main/main.component.ts b/src/main/webapp/app/app.component.ts similarity index 76% rename from src/main/webapp/app/shared/layouts/main/main.component.ts rename to src/main/webapp/app/app.component.ts index 299eebd09926..c09f0685cb86 100644 --- a/src/main/webapp/app/shared/layouts/main/main.component.ts +++ b/src/main/webapp/app/app.component.ts @@ -1,32 +1,49 @@ -import { Component, Inject, OnDestroy, OnInit, Renderer2 } from '@angular/core'; -import { ActivatedRouteSnapshot, NavigationEnd, NavigationError, NavigationStart, Router } from '@angular/router'; +import { Component, OnDestroy, OnInit, Renderer2, inject } from '@angular/core'; +import { ActivatedRouteSnapshot, NavigationEnd, NavigationError, NavigationStart, Router, RouterOutlet } from '@angular/router'; import { JhiLanguageHelper } from 'app/core/language/language.helper'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { SentryErrorHandler } from 'app/core/sentry/sentry.error-handler'; import { ThemeService } from 'app/core/theme/theme.service'; -import { DOCUMENT } from '@angular/common'; +import { DOCUMENT, NgClass, NgStyle } from '@angular/common'; import { Subscription } from 'rxjs'; import { ExamParticipationService } from 'app/exam/participate/exam-participation.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { LtiService } from 'app/shared/service/lti.service'; +import { AlertOverlayComponent } from './shared/alert/alert-overlay.component'; +import { CdkScrollable } from '@angular/cdk/scrolling'; +import { PageRibbonComponent } from './shared/layouts/profiles/page-ribbon.component'; +import { NotificationPopupComponent } from './shared/notification/notification-popup/notification-popup.component'; +import { FooterComponent } from './shared/layouts/footer/footer.component'; @Component({ - selector: 'jhi-main', - templateUrl: './main.component.html', - styleUrls: ['./main.component.scss'], + selector: 'jhi-app', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'], + imports: [AlertOverlayComponent, CdkScrollable, NgClass, NgStyle, PageRibbonComponent, RouterOutlet, NotificationPopupComponent, FooterComponent], }) -export class JhiMainComponent implements OnInit, OnDestroy { +export class AppComponent implements OnInit, OnDestroy { + private jhiLanguageHelper = inject(JhiLanguageHelper); + private router = inject(Router); + private profileService = inject(ProfileService); + private examParticipationService = inject(ExamParticipationService); + private sentryErrorHandler = inject(SentryErrorHandler); + private themeService = inject(ThemeService); + private document = inject(DOCUMENT); + private renderer = inject(Renderer2); + private courseService = inject(CourseManagementService); + private ltiService = inject(LtiService); + + private profileSubscription: Subscription; + private examStartedSubscription: Subscription; + private courseOverviewSubscription: Subscription; + private testRunSubscription: Subscription; + private ltiSubscription: Subscription; /** * If the footer and header should be shown. * Only set to false on specific pages designed for the native Android and iOS applications where the footer and header are not wanted. * The decision on whether to show the skeleton or not for a specific route is defined in shouldShowSkeleton. */ - public showSkeleton = true; - profileSubscription: Subscription; - examStartedSubscription: Subscription; - courseOverviewSubscription: Subscription; - testRunSubscription: Subscription; - ltiSubscription: Subscription; + showSkeleton = true; isProduction = true; isTestServer = false; isExamStarted = false; @@ -34,19 +51,7 @@ export class JhiMainComponent implements OnInit, OnDestroy { isCourseOverview = false; isShownViaLti = false; - constructor( - private jhiLanguageHelper: JhiLanguageHelper, - private router: Router, - private profileService: ProfileService, - private examParticipationService: ExamParticipationService, - private sentryErrorHandler: SentryErrorHandler, - private themeService: ThemeService, - @Inject(DOCUMENT) - private document: Document, - private renderer: Renderer2, - private courseService: CourseManagementService, - private ltiService: LtiService, - ) { + constructor() { this.setupErrorHandling().then(undefined); } diff --git a/src/main/webapp/app/app.config.ts b/src/main/webapp/app/app.config.ts new file mode 100644 index 000000000000..90ee4be97e9f --- /dev/null +++ b/src/main/webapp/app/app.config.ts @@ -0,0 +1,128 @@ +import './polyfills'; +import 'app/shared/util/array.extension'; +import 'app/shared/util/map.extension'; +import 'app/shared/util/string.extension'; +import 'app/core/config/dayjs'; +import { ScrollingModule } from '@angular/cdk/scrolling'; +import { DatePipe } from '@angular/common'; +import { HTTP_INTERCEPTORS, HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { ApplicationConfig, ErrorHandler, LOCALE_ID, importProvidersFrom, inject, provideAppInitializer } from '@angular/core'; +import { BrowserModule, Title } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { Router, RouterModule, provideRouter, withRouterConfig } from '@angular/router'; +import { ServiceWorkerModule } from '@angular/service-worker'; +import { NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap'; +import { MissingTranslationHandler, TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import * as Sentry from '@sentry/angular'; +import { TraceService } from '@sentry/angular'; +import routes from 'app/app.routes'; +import { NgbDateDayjsAdapter } from 'app/core/config/datepicker-adapter'; +import { missingTranslationHandler, translatePartialLoader } from 'app/core/config/translation.config'; +import { ArtemisVersionInterceptor, WINDOW_INJECTOR_TOKEN } from 'app/core/interceptor/artemis-version.interceptor'; +import { AuthExpiredInterceptor } from 'app/core/interceptor/auth-expired.interceptor'; +import { BrowserFingerprintInterceptor } from 'app/core/interceptor/browser-fingerprint.interceptor.service'; +import { ErrorHandlerInterceptor } from 'app/core/interceptor/errorhandler.interceptor'; +import { NotificationInterceptor } from 'app/core/interceptor/notification.interceptor'; +import { SentryErrorHandler } from 'app/core/sentry/sentry.error-handler'; +import { GuidedTourModule } from 'app/guided-tour/guided-tour.module'; +import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; +import { LoadingNotificationInterceptor } from 'app/shared/notification/loading-notification/loading-notification.interceptor'; +import { OrionConnectorService } from 'app/shared/orion/orion-connector.service'; +import { ArtemisSharedModule } from 'app/shared/shared.module'; +import { UserSettingsModule } from 'app/shared/user-settings/user-settings.module'; +import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; +import { provideNgxWebstorage, withLocalStorage, withNgxWebstorageConfig, withSessionStorage } from 'ngx-webstorage'; + +export function initOrionConnector(connector: OrionConnectorService) { + return () => OrionConnectorService.initConnector(connector); +} + +export const appConfig: ApplicationConfig = { + providers: [ + importProvidersFrom( + // TODO: we should exclude modules here in the future + ArtemisSharedComponentModule, + ArtemisSharedModule, + BrowserAnimationsModule, + BrowserModule, + GuidedTourModule, + RouterModule, + ScrollingModule, + UserSettingsModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: translatePartialLoader, + deps: [HttpClient], + }, + missingTranslationHandler: { + provide: MissingTranslationHandler, + useFactory: missingTranslationHandler, + }, + }), + ), + + // TODO: we should add withComponentInputBinding here + // this would set non-route inputs to undefined, which not all components can handle, currently + // see https://angular.dev/api/router/withComponentInputBinding?tab=usage-notes + // provideRouter(routes, withComponentInputBinding(), withRouterConfig({ onSameUrlNavigation: 'reload' })), + provideRouter(routes, withRouterConfig({ onSameUrlNavigation: 'reload' })), + // This enables service worker (PWA) + importProvidersFrom(ServiceWorkerModule.register('ngsw-worker.js', { enabled: true })), + provideHttpClient(withInterceptorsFromDi()), + provideNgxWebstorage(withNgxWebstorageConfig({ prefix: 'jhi', separator: '-' }), withLocalStorage(), withSessionStorage()), + Title, + { provide: LOCALE_ID, useValue: 'en' }, + { provide: NgbDateAdapter, useClass: NgbDateDayjsAdapter }, + { provide: Sentry.TraceService, deps: [Router] }, + { provide: ErrorHandler, useClass: SentryErrorHandler }, + { provide: WINDOW_INJECTOR_TOKEN, useValue: window }, + DatePipe, + provideAppInitializer(() => { + inject(TraceService); + // Ensure the service is initialized before any routing happens + inject(ArtemisNavigationUtilService); + // Required, otherwise Orion will not work at all + initOrionConnector(inject(OrionConnectorService)); + }), + /** + * @description Interceptor declarations: + * Interceptors are located at 'blocks/interceptor/. + * All of them implement the HttpInterceptor interface. + * They can be used to modify API calls or trigger additional function calls. + * Most interceptors will transform the outgoing request before passing it to + * the next interceptor in the chain, by calling next.handle(transformedReq). + * Documentation: https://angular.io/api/common/http/HttpInterceptor + */ + { + provide: HTTP_INTERCEPTORS, + useClass: AuthExpiredInterceptor, + multi: true, + }, + { + provide: HTTP_INTERCEPTORS, + useClass: ErrorHandlerInterceptor, + multi: true, + }, + { + provide: HTTP_INTERCEPTORS, + useClass: BrowserFingerprintInterceptor, + multi: true, + }, + { + provide: HTTP_INTERCEPTORS, + useClass: NotificationInterceptor, + multi: true, + }, + { + provide: HTTP_INTERCEPTORS, + useClass: LoadingNotificationInterceptor, + multi: true, + }, + { + provide: HTTP_INTERCEPTORS, + useClass: ArtemisVersionInterceptor, + multi: true, + }, + ], +}; diff --git a/src/main/webapp/app/app.main.ts b/src/main/webapp/app/app.main.ts index 1c2b479d3a8a..cf1c49919310 100644 --- a/src/main/webapp/app/app.main.ts +++ b/src/main/webapp/app/app.main.ts @@ -1,16 +1,42 @@ -import './polyfills'; -import 'app/shared/util/array.extension'; -import 'app/shared/util/map.extension'; -import 'app/shared/util/string.extension'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { ProdConfig } from './core/config/prod.config'; -import { ArtemisAppModule } from './app.module'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from 'app/app.config'; import { MonacoConfig } from 'app/core/config/monaco.config'; +import { ProdConfig } from 'app/core/config/prod.config'; +import { JhiLanguageHelper } from 'app/core/language/language.helper'; +import { artemisIconPack } from 'app/icons/icons'; +import { AppComponent } from './app.component'; +import { FaIconLibrary } from '@fortawesome/angular-fontawesome'; +import isMobile from 'ismobilejs-es5'; +import { registerLocaleData } from '@angular/common'; +import locale from '@angular/common/locales/en'; +import dayjs from 'dayjs/esm'; +import { NgbDatepickerConfig, NgbTooltipConfig } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { SessionStorageService } from 'ngx-webstorage'; ProdConfig(); MonacoConfig(); -platformBrowserDynamic() - .bootstrapModule(ArtemisAppModule, { preserveWhitespaces: true }) - .then(() => {}) +bootstrapApplication(AppComponent, appConfig) + .then((app) => { + // TODO: potentially move this code into AppComponent + const library = app.injector.get(FaIconLibrary); + library.addIconPacks(artemisIconPack); + const dpConfig = app.injector.get(NgbDatepickerConfig); + const tooltipConfig = app.injector.get(NgbTooltipConfig); + const translateService = app.injector.get(TranslateService); + const languageHelper = app.injector.get(JhiLanguageHelper); + const sessionStorageService = app.injector.get(SessionStorageService); + + // Perform initialization logic + registerLocaleData(locale); + dpConfig.minDate = { year: dayjs().subtract(100, 'year').year(), month: 1, day: 1 }; + translateService.setDefaultLang('en'); + const languageKey = sessionStorageService.retrieve('locale') || languageHelper.determinePreferredLanguage(); + translateService.use(languageKey); + tooltipConfig.container = 'body'; + if (isMobile(window.navigator.userAgent).any ?? false) { + tooltipConfig.disableTooltip = true; + } + }) .catch((err) => console.error(err)); diff --git a/src/main/webapp/app/app.module.ts b/src/main/webapp/app/app.module.ts deleted file mode 100644 index 1e43beff4fc1..000000000000 --- a/src/main/webapp/app/app.module.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; -import { ServiceWorkerModule } from '@angular/service-worker'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { ArtemisSystemNotificationModule } from 'app/shared/notification/system-notification/system-notification.module'; -import { NavbarComponent } from 'app/shared/layouts/navbar/navbar.component'; -import { NotificationSidebarComponent } from 'app/shared/notification/notification-sidebar/notification-sidebar.component'; -import { PageRibbonComponent } from 'app/shared/layouts/profiles/page-ribbon.component'; -import { ArtemisHeaderExercisePageWithDetailsModule } from 'app/exercises/shared/exercise-headers/exercise-headers.module'; -import { SystemNotificationComponent } from 'app/shared/notification/system-notification/system-notification.component'; -import { ArtemisAppRoutingModule } from 'app/app-routing.module'; -import { JhiMainComponent } from 'app/shared/layouts/main/main.component'; -import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { FooterComponent } from 'app/shared/layouts/footer/footer.component'; -import { ActiveMenuDirective } from 'app/shared/layouts/navbar/active-menu.directive'; -import { ErrorComponent } from 'app/shared/layouts/error/error.component'; -import { ArtemisCoreModule } from 'app/core/core.module'; -import { GuidedTourModule } from 'app/guided-tour/guided-tour.module'; -import { ArtemisComplaintsModule } from 'app/complaints/complaints.module'; -import { OrionOutdatedComponent } from 'app/shared/orion/outdated-plugin-warning/orion-outdated.component'; -import { LoadingNotificationComponent } from 'app/shared/notification/loading-notification/loading-notification.component'; -import { NotificationPopupComponent } from 'app/shared/notification/notification-popup/notification-popup.component'; -import { UserSettingsModule } from 'app/shared/user-settings/user-settings.module'; -import { ThemeSwitchComponent } from 'app/core/theme/theme-switch.component'; -import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; -import { FaIconLibrary } from '@fortawesome/angular-fontawesome'; -import { artemisIconPack } from 'src/main/webapp/content/icons/icons'; -import { ScrollingModule } from '@angular/cdk/scrolling'; - -// NOTE: this module should only include the most important modules for normal users, all course management, admin and account functionality should be lazy loaded if possible -@NgModule({ - imports: [ - BrowserModule, - BrowserAnimationsModule, - // This enables service worker (PWA) - ServiceWorkerModule.register('ngsw-worker.js', { enabled: true }), - ArtemisSharedModule, - ArtemisCoreModule, - ArtemisAppRoutingModule, - GuidedTourModule, - ArtemisSystemNotificationModule, - ArtemisComplaintsModule, - ArtemisHeaderExercisePageWithDetailsModule, - UserSettingsModule, - ThemeSwitchComponent, - ArtemisSharedComponentModule, - ScrollingModule, - ], - declarations: [ - JhiMainComponent, - NavbarComponent, - ErrorComponent, - OrionOutdatedComponent, - PageRibbonComponent, - ActiveMenuDirective, - FooterComponent, - NotificationPopupComponent, - NotificationSidebarComponent, - SystemNotificationComponent, - LoadingNotificationComponent, - ], - bootstrap: [JhiMainComponent], -}) -export class ArtemisAppModule { - constructor(library: FaIconLibrary) { - library.addIconPacks(artemisIconPack); - } -} diff --git a/src/main/webapp/app/app.routes.ts b/src/main/webapp/app/app.routes.ts new file mode 100644 index 000000000000..214a653bf01c --- /dev/null +++ b/src/main/webapp/app/app.routes.ts @@ -0,0 +1,185 @@ +import { Routes } from '@angular/router'; +import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; +import { Authority } from 'app/shared/constants/authority.constants'; +import { navbarRoute } from 'app/shared/layouts/navbar/navbar.route'; +import { errorRoute } from 'app/shared/layouts/error/error.route'; + +const LAYOUT_ROUTES: Routes = [navbarRoute, ...errorRoute]; + +const routes: Routes = [ + ...LAYOUT_ROUTES, + { + path: '', + loadComponent: () => import('./home/home.component').then((m) => m.HomeComponent), + data: { + pageTitle: 'home.title', + }, + }, + { + path: 'admin', + loadChildren: () => import('./admin/admin.module').then((m) => m.ArtemisAdminModule), + }, + { + path: 'privacy', + loadChildren: () => import('./core/legal/privacy.module').then((m) => m.ArtemisPrivacyModule), + }, + { + path: 'imprint', + loadChildren: () => import('./core/legal/imprint.module').then((m) => m.ArtemisImprintModule), + }, + { + path: 'about', + loadChildren: () => import('./core/about-us/artemis-about-us.module').then((module) => module.ArtemisAboutUsModule), + }, + // ===== TEAM ==== + { + path: 'course-management/:courseId/exercises/:exerciseId/teams', + loadChildren: () => import('./exercises/shared/team/team.module').then((m) => m.ArtemisTeamModule), + }, + { + path: 'courses/:courseId/exercises/:exerciseId/teams', + loadChildren: () => import('./exercises/shared/team/team.module').then((m) => m.ArtemisTeamModule), + }, + // ===== ACCOUNT ==== + { + path: 'account', + children: [ + { + path: 'activate', + pathMatch: 'full', + loadComponent: () => import('./account/activate/activate.component').then((m) => m.ActivateComponent), + data: { + pageTitle: 'activate.title', + }, + }, + { + path: 'password', + pathMatch: 'full', + loadComponent: () => import('./account/password/password.component').then((m) => m.PasswordComponent), + data: { + authorities: [Authority.USER], + pageTitle: 'global.menu.account.password', + }, + canActivate: [UserRouteAccessService], + }, + { + path: 'reset/finish', + pathMatch: 'full', + loadComponent: () => import('./account/password-reset/finish/password-reset-finish.component').then((m) => m.PasswordResetFinishComponent), + data: { + pageTitle: 'global.menu.account.password', + }, + }, + { + path: 'reset/request', + pathMatch: 'full', + loadComponent: () => import('./account/password-reset/init/password-reset-init.component').then((m) => m.PasswordResetInitComponent), + data: { + pageTitle: 'global.menu.account.password', + }, + }, + { + path: 'register', + pathMatch: 'full', + loadComponent: () => import('./account/register/register.component').then((m) => m.RegisterComponent), + data: { + pageTitle: 'register.title', + }, + }, + { + path: 'settings', + pathMatch: 'full', + loadComponent: () => import('./account/settings/settings.component').then((m) => m.SettingsComponent), + data: { + authorities: [Authority.USER], + pageTitle: 'global.menu.account.settings', + }, + canActivate: [UserRouteAccessService], + }, + ], + }, + // ===== COURSE MANAGEMENT ===== + { + path: 'course-management', + loadChildren: () => import('./course/manage/course-management.module').then((m) => m.ArtemisCourseManagementModule), + }, + { + path: 'course-management/:courseId/programming-exercises/:exerciseId/code-editor', + loadChildren: () => import('./exercises/programming/manage/code-editor/code-editor-management.module').then((m) => m.ArtemisCodeEditorManagementModule), + }, + { + path: 'course-management/:courseId/text-exercises/:exerciseId', + loadChildren: () => import('./exercises/text/assess/text-submission-assessment.module').then((m) => m.ArtemisTextSubmissionAssessmentModule), + }, + { + path: 'course-management/:courseId/text-exercises/:exerciseId/example-submissions/:exampleSubmissionId', + loadChildren: () => import('./exercises/text/manage/example-text-submission/example-text-submission.module').then((m) => m.ArtemisExampleTextSubmissionModule), + }, + { + path: 'course-management/:courseId/programming-exercises/:exerciseId', + loadChildren: () => + import('./exercises/programming/manage/programming-exercise-management-routing.module').then((m) => m.ArtemisProgrammingExerciseManagementRoutingModule), + }, + { + path: 'courses', + loadChildren: () => import('./overview/courses.module').then((m) => m.ArtemisCoursesModule), + }, + { + path: 'course-management/:courseId/lectures/:lectureId/attachments/:attachmentId', + pathMatch: 'full', + loadComponent: () => import('./lecture/pdf-preview/pdf-preview.component').then((m) => m.PdfPreviewComponent), + }, + // ===== GRADING SYSTEM ===== + { + path: 'courses/:courseId/grading-system', + loadChildren: () => import('./grading-system/grading-system.module').then((m) => m.GradingSystemModule), + }, + + { + path: 'courses/:courseId/exercises/:exerciseId/problem-statement', + pathMatch: 'full', + loadComponent: () => import('./overview/exercise-details/problem-statement/problem-statement.component').then((m) => m.ProblemStatementComponent), + }, + { + pathMatch: 'full', + path: 'courses/:courseId/exercises/:exerciseId/problem-statement/:participationId', + loadComponent: () => import('./overview/exercise-details/problem-statement/problem-statement.component').then((m) => m.ProblemStatementComponent), + }, + { + path: 'courses/:courseId/exercises/:exerciseId/participations/:participationId/results/:resultId/feedback', + pathMatch: 'full', + loadComponent: () => import('./exercises/shared/feedback/standalone-feedback/standalone-feedback.component').then((m) => m.StandaloneFeedbackComponent), + }, + + // ===== EXAM ===== + { + path: 'course-management/:courseId/exams', + loadChildren: () => import('./exam/manage/exam-management.module').then((m) => m.ArtemisExamManagementModule), + }, + { + path: 'courses/:courseId/exams/:examId/grading-system', + loadChildren: () => import('./grading-system/grading-system.module').then((m) => m.GradingSystemModule), + }, + { + path: 'courses/:courseId/exams/:examId/exercises/:exerciseId/repository', + loadChildren: () => import('./exercises/programming/participate/programming-repository-routing.module').then((m) => m.ArtemisProgrammingRepositoryRoutingModule), + }, + { + path: 'features', + loadChildren: () => import('./feature-overview/feature-overview.module').then((m) => m.FeatureOverviewModule), + }, + { + path: 'lti', + loadChildren: () => import('./lti/lti.module').then((m) => m.ArtemisLtiModule), + }, + { + path: 'about-iris', + pathMatch: 'full', + loadComponent: () => import('./iris/about-iris/about-iris.component').then((m) => m.AboutIrisComponent), + data: { + pageTitle: 'artemisApp.exerciseChatbot.title', + }, + }, +]; + +export default routes; diff --git a/src/main/webapp/app/assessment/assessment-complaint-alert/assessment-complaint-alert.component.ts b/src/main/webapp/app/assessment/assessment-complaint-alert/assessment-complaint-alert.component.ts index 96be65f50ded..55c048949f44 100644 --- a/src/main/webapp/app/assessment/assessment-complaint-alert/assessment-complaint-alert.component.ts +++ b/src/main/webapp/app/assessment/assessment-complaint-alert/assessment-complaint-alert.component.ts @@ -1,5 +1,6 @@ import { Component, Input } from '@angular/core'; import { Complaint, ComplaintType } from 'app/entities/complaint.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; /** * This shows an alert, notifying the assessor on possible complaints at the bottom of the page. @@ -8,6 +9,7 @@ import { Complaint, ComplaintType } from 'app/entities/complaint.model'; selector: 'jhi-assessment-complaint-alert', templateUrl: './assessment-complaint-alert.component.html', styleUrls: [], + imports: [TranslateDirective], }) export class AssessmentComplaintAlertComponent { ComplaintType = ComplaintType; diff --git a/src/main/webapp/app/assessment/assessment-header/assessment-header.component.ts b/src/main/webapp/app/assessment/assessment-header/assessment-header.component.ts index 6e6dc93aff16..a3a87c32d514 100644 --- a/src/main/webapp/app/assessment/assessment-header/assessment-header.component.ts +++ b/src/main/webapp/app/assessment/assessment-header/assessment-header.component.ts @@ -1,14 +1,19 @@ -import { Component, EventEmitter, HostListener, Input, Output } from '@angular/core'; +import { Component, EventEmitter, HostListener, Input, Output, inject } from '@angular/core'; import { Result } from 'app/entities/result.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { TextAssessmentAnalytics } from 'app/exercises/text/assess/analytics/text-assesment-analytics.service'; import { TextAssessmentEventType } from 'app/entities/text/text-assesment-event.model'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { ComplaintType } from 'app/entities/complaint.model'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { TranslateService } from '@ngx-translate/core'; import { faSave, faSpinner } from '@fortawesome/free-solid-svg-icons'; import { faSquareCaretRight } from '@fortawesome/free-regular-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbAlert, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { AssessmentWarningComponent } from '../assessment-warning/assessment-warning.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; /** * The component is used in the shared assessment layout. @@ -20,8 +25,13 @@ import { faSquareCaretRight } from '@fortawesome/free-regular-svg-icons'; selector: 'jhi-assessment-header', templateUrl: './assessment-header.component.html', styleUrls: ['./assessment-header.component.scss'], + imports: [TranslateDirective, NgbAlert, NgbTooltip, FaIconComponent, RouterLink, AssessmentWarningComponent, ArtemisTranslatePipe], }) export class AssessmentHeaderComponent { + textAssessmentAnalytics = inject(TextAssessmentAnalytics); + protected route = inject(ActivatedRoute); + private translateService = inject(TranslateService); + @Input() isLoading: boolean; @Input() saveBusy: boolean; @Input() submitBusy: boolean; @@ -68,12 +78,8 @@ export class AssessmentHeaderComponent { this.highlightDifferencesChange.emit(this.highlightDifferences); } - constructor( - public textAssessmentAnalytics: TextAssessmentAnalytics, - protected route: ActivatedRoute, - private translateService: TranslateService, - ) { - textAssessmentAnalytics.setComponentRoute(route); + constructor() { + this.textAssessmentAnalytics.setComponentRoute(this.route); } get highlightDifferences() { diff --git a/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions.module.ts b/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions.module.ts index cbd75c22898e..94ccc9b6673c 100644 --- a/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions.module.ts +++ b/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions.module.ts @@ -23,8 +23,6 @@ import { OrionAssessmentInstructionsComponent } from 'app/orion/assessment/orion ArtemisAssessmentSharedModule, ArtemisSharedComponentModule, ArtemisMarkdownModule, - ], - declarations: [ ExpandableSectionComponent, AssessmentInstructionsComponent, OrionAssessmentInstructionsComponent, diff --git a/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.html b/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.html index 7f1b0410c943..6a46e7df1762 100644 --- a/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.html +++ b/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.html @@ -12,7 +12,9 @@ } } -

    {{ exercise.title }}

    + + +

    {{ exercise.title }}

    @switch (exercise.type) { @case (ExerciseType.PROGRAMMING) { diff --git a/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.ts b/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.ts index d854c6f7ad58..846f4a774e8d 100644 --- a/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.ts +++ b/src/main/webapp/app/assessment/assessment-instructions/assessment-instructions/assessment-instructions.component.ts @@ -1,4 +1,4 @@ -import { Component, ContentChild, Input, TemplateRef } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { SafeHtml } from '@angular/platform-browser'; import { UMLDiagramType, UMLModel } from '@ls1intum/apollon'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; @@ -9,12 +9,32 @@ import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { GradingCriterion } from 'app/exercises/shared/structured-grading-criterion/grading-criterion.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; +import { ExpandableSectionComponent } from '../expandable-section/expandable-section.component'; +import { StructuredGradingInstructionsAssessmentLayoutComponent } from '../../structured-grading-instructions-assessment-layout/structured-grading-instructions-assessment-layout.component'; +import { ProgrammingExerciseInstructionComponent } from 'app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component'; +import { SecureLinkDirective } from 'app/shared/http/secure-link.directive'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; @Component({ selector: 'jhi-assessment-instructions', templateUrl: './assessment-instructions.component.html', + imports: [ + ExpandableSectionComponent, + StructuredGradingInstructionsAssessmentLayoutComponent, + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // ExtensionPointDirective, + ProgrammingExerciseInstructionComponent, + SecureLinkDirective, + ButtonComponent, + TranslateDirective, + ModelingEditorComponent, + ], }) export class AssessmentInstructionsComponent { + private markdownService = inject(ArtemisMarkdownService); + exercise: Exercise; programmingExercise?: ProgrammingExercise; problemStatement: SafeHtml; @@ -34,9 +54,8 @@ export class AssessmentInstructionsComponent { readonly ExerciseType = ExerciseType; // extension points, see shared/extension-point - @ContentChild('overrideTitle') overrideTitle: TemplateRef; - - constructor(private markdownService: ArtemisMarkdownService) {} + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // @ContentChild('overrideTitle') overrideTitle: TemplateRef; // eslint-disable-next-line @angular-eslint/no-input-rename @Input('exercise') set exerciseInput(exercise: Exercise) { diff --git a/src/main/webapp/app/assessment/assessment-instructions/collapsable-assessment-instructions/collapsable-assessment-instructions.component.ts b/src/main/webapp/app/assessment/assessment-instructions/collapsable-assessment-instructions/collapsable-assessment-instructions.component.ts index 3d480876a182..6ff4469e71c1 100644 --- a/src/main/webapp/app/assessment/assessment-instructions/collapsable-assessment-instructions/collapsable-assessment-instructions.component.ts +++ b/src/main/webapp/app/assessment/assessment-instructions/collapsable-assessment-instructions/collapsable-assessment-instructions.component.ts @@ -3,11 +3,15 @@ import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { faChevronLeft, faChevronRight, faGripLinesVertical } from '@fortawesome/free-solid-svg-icons'; import { Exercise } from 'app/entities/exercise.model'; import interact from 'interactjs'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { AssessmentInstructionsComponent } from '../assessment-instructions/assessment-instructions.component'; @Component({ selector: 'jhi-collapsable-assessment-instructions', templateUrl: './collapsable-assessment-instructions.component.html', styleUrls: ['./collapsable-assessment-instructions.scss'], + imports: [FaIconComponent, TranslateDirective, AssessmentInstructionsComponent], }) export class CollapsableAssessmentInstructionsComponent implements AfterViewInit { @Input() isAssessmentTraining = false; diff --git a/src/main/webapp/app/assessment/assessment-instructions/expandable-section/expandable-section.component.ts b/src/main/webapp/app/assessment/assessment-instructions/expandable-section/expandable-section.component.ts index ff8cb7035f0f..8c5a88074148 100644 --- a/src/main/webapp/app/assessment/assessment-instructions/expandable-section/expandable-section.component.ts +++ b/src/main/webapp/app/assessment/assessment-instructions/expandable-section/expandable-section.component.ts @@ -1,12 +1,18 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { faAngleDown, faAngleRight } from '@fortawesome/free-solid-svg-icons'; import { LocalStorageService } from 'ngx-webstorage'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-expandable-section', templateUrl: './expandable-section.component.html', + imports: [FaIconComponent, NgbCollapse, ArtemisTranslatePipe], }) export class ExpandableSectionComponent implements OnInit { + private localStorageService = inject(LocalStorageService); + @Input() headerKey: string; @Input() hasTranslation = true; @Input() isSubHeader = false; @@ -18,8 +24,6 @@ export class ExpandableSectionComponent implements OnInit { readonly PREFIX = 'collapsed.'; - constructor(private localStorageService: LocalStorageService) {} - ngOnInit(): void { this.isCollapsed = !!this.localStorageService.retrieve(this.storageKey); this.localStorageService.store(this.storageKey, this.isCollapsed); diff --git a/src/main/webapp/app/assessment/assessment-layout/assessment-layout.component.ts b/src/main/webapp/app/assessment/assessment-layout/assessment-layout.component.ts index b68902048248..e82b6597c9f0 100644 --- a/src/main/webapp/app/assessment/assessment-layout/assessment-layout.component.ts +++ b/src/main/webapp/app/assessment/assessment-layout/assessment-layout.component.ts @@ -5,6 +5,10 @@ import { Exercise } from 'app/entities/exercise.model'; import { Submission } from 'app/entities/submission.model'; import { AssessmentAfterComplaint } from 'app/complaints/complaints-for-tutor/complaints-for-tutor.component'; import { AssessmentNote } from 'app/entities/assessment-note.model'; +import { AssessmentHeaderComponent } from '../assessment-header/assessment-header.component'; +import { AssessmentComplaintAlertComponent } from '../assessment-complaint-alert/assessment-complaint-alert.component'; +import { AssessmentNoteComponent } from '../assessment-note/assessment-note.component'; +import { ComplaintsForTutorComponent } from '../../complaints/complaints-for-tutor/complaints-for-tutor.component'; /** * The component provides the basic layout for an assessment page. @@ -16,6 +20,7 @@ import { AssessmentNote } from 'app/entities/assessment-note.model'; selector: 'jhi-assessment-layout', templateUrl: './assessment-layout.component.html', styleUrls: ['./assessment-layout.component.scss'], + imports: [AssessmentHeaderComponent, AssessmentComplaintAlertComponent, AssessmentNoteComponent, ComplaintsForTutorComponent], }) export class AssessmentLayoutComponent { @HostBinding('class.assessment-container') readonly assessmentContainerClass = true; diff --git a/src/main/webapp/app/assessment/assessment-locks/assessment-locks.component.ts b/src/main/webapp/app/assessment/assessment-locks/assessment-locks.component.ts index 78b63339c2ad..b44aeac5ba83 100644 --- a/src/main/webapp/app/assessment/assessment-locks/assessment-locks.component.ts +++ b/src/main/webapp/app/assessment/assessment-locks/assessment-locks.component.ts @@ -1,5 +1,5 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { FileUploadAssessmentService } from 'app/exercises/file-upload/assess/file-upload-assessment.service'; import { TranslateService } from '@ngx-translate/core'; import { Submission, SubmissionExerciseType } from 'app/entities/submission.model'; @@ -14,12 +14,27 @@ import { ProgrammingAssessmentManualResultService } from 'app/exercises/programm import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { faBan, faFolderOpen } from '@fortawesome/free-solid-svg-icons'; import { combineLatest } from 'rxjs'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-assessment-locks', templateUrl: './assessment-locks.component.html', + imports: [TranslateDirective, FaIconComponent, NgbTooltip, RouterLink, ArtemisDatePipe, ArtemisTranslatePipe], }) export class AssessmentLocksComponent implements OnInit { + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + private modelingAssessmentService = inject(ModelingAssessmentService); + private textAssessmentService = inject(TextAssessmentService); + private fileUploadAssessmentService = inject(FileUploadAssessmentService); + private programmingAssessmentService = inject(ProgrammingAssessmentManualResultService); + private courseService = inject(CourseManagementService); + private examManagementService = inject(ExamManagementService); + readonly ExerciseType = ExerciseType; course: Course; @@ -40,17 +55,8 @@ export class AssessmentLocksComponent implements OnInit { faBan = faBan; faFolderOpen = faFolderOpen; - constructor( - private route: ActivatedRoute, - private alertService: AlertService, - private modelingAssessmentService: ModelingAssessmentService, - private textAssessmentService: TextAssessmentService, - private fileUploadAssessmentService: FileUploadAssessmentService, - private programmingAssessmentService: ProgrammingAssessmentManualResultService, - translateService: TranslateService, - private courseService: CourseManagementService, - private examManagementService: ExamManagementService, - ) { + constructor() { + const translateService = inject(TranslateService); translateService.get('artemisApp.assessment.messages.confirmCancel').subscribe((text) => (this.cancelConfirmationText = text)); } diff --git a/src/main/webapp/app/assessment/assessment-locks/assessment-locks.route.ts b/src/main/webapp/app/assessment/assessment-locks/assessment-locks.route.ts index c2d1221363fa..fa4b9e2319d2 100644 --- a/src/main/webapp/app/assessment/assessment-locks/assessment-locks.route.ts +++ b/src/main/webapp/app/assessment/assessment-locks/assessment-locks.route.ts @@ -1,13 +1,13 @@ import { Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { AssessmentLocksComponent } from './assessment-locks.component'; + import { Authority } from 'app/shared/constants/authority.constants'; import { CourseManagementResolve } from 'app/course/manage/course-management-resolve.service'; export const assessmentLocksRoute: Routes = [ { path: ':courseId/exams/:examId/assessment-locks', - component: AssessmentLocksComponent, + loadComponent: () => import('./assessment-locks.component').then((m) => m.AssessmentLocksComponent), resolve: { course: CourseManagementResolve, }, @@ -19,7 +19,7 @@ export const assessmentLocksRoute: Routes = [ }, { path: ':courseId/assessment-locks', - component: AssessmentLocksComponent, + loadComponent: () => import('./assessment-locks.component').then((m) => m.AssessmentLocksComponent), resolve: { course: CourseManagementResolve, }, diff --git a/src/main/webapp/app/assessment/assessment-note/assessment-note.component.ts b/src/main/webapp/app/assessment/assessment-note/assessment-note.component.ts index f21ad66262cc..e545e745ecfa 100644 --- a/src/main/webapp/app/assessment/assessment-note/assessment-note.component.ts +++ b/src/main/webapp/app/assessment/assessment-note/assessment-note.component.ts @@ -1,10 +1,12 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { AssessmentNote } from 'app/entities/assessment-note.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-assessment-note', templateUrl: './assessment-note.component.html', styleUrls: ['./assessment-note.component.scss'], + imports: [TranslateDirective], }) export class AssessmentNoteComponent { private _assessmentNote: AssessmentNote; diff --git a/src/main/webapp/app/assessment/assessment-shared.module.ts b/src/main/webapp/app/assessment/assessment-shared.module.ts index fe386487df8c..0677f2d0c174 100644 --- a/src/main/webapp/app/assessment/assessment-shared.module.ts +++ b/src/main/webapp/app/assessment/assessment-shared.module.ts @@ -32,8 +32,6 @@ const ENTITY_STATES = [...assessmentLocksRoute]; ArtemisFeedbackModule, FeedbackContentPipe, QuotePipe, - ], - declarations: [ AssessmentHeaderComponent, AssessmentLayoutComponent, AssessmentComplaintAlertComponent, diff --git a/src/main/webapp/app/assessment/assessment-warning/assessment-warning.component.ts b/src/main/webapp/app/assessment/assessment-warning/assessment-warning.component.ts index 42120b935660..3a1eeb2f874e 100644 --- a/src/main/webapp/app/assessment/assessment-warning/assessment-warning.component.ts +++ b/src/main/webapp/app/assessment/assessment-warning/assessment-warning.component.ts @@ -3,6 +3,8 @@ import dayjs from 'dayjs/esm'; import { Exercise } from 'app/entities/exercise.model'; import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; import { Submission } from 'app/entities/submission.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; /** * Displays a warning for instructors on submission page, team page and the assessment page. @@ -26,6 +28,7 @@ import { Submission } from 'app/entities/submission.model'; } `, + imports: [FaIconComponent, TranslateDirective], }) export class AssessmentWarningComponent implements OnChanges { @Input() exercise: Exercise; @@ -40,7 +43,7 @@ export class AssessmentWarningComponent implements OnChanges { /** * Checks if the due date of the exercise is over */ - ngOnChanges(): void { + ngOnChanges() { if (this.exercise.dueDate) { const now = dayjs(); this.isBeforeExerciseDueDate = now.isBefore(this.exercise.dueDate); diff --git a/src/main/webapp/app/assessment/athena.service.ts b/src/main/webapp/app/assessment/athena.service.ts index 436330e568f2..0488997d035d 100644 --- a/src/main/webapp/app/assessment/athena.service.ts +++ b/src/main/webapp/app/assessment/athena.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable, map, of, switchMap } from 'rxjs'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; @@ -13,12 +13,10 @@ import { ModelingSubmission } from 'app/entities/modeling-submission.model'; @Injectable({ providedIn: 'root' }) export class AthenaService { - public resourceUrl = 'api/athena'; + protected http = inject(HttpClient); + private profileService = inject(ProfileService); - constructor( - protected http: HttpClient, - private profileService: ProfileService, - ) {} + public resourceUrl = 'api/athena'; /** * Determine if the Athena service is available based on whether the corresponding profile is active diff --git a/src/main/webapp/app/assessment/structured-grading-instructions-assessment-layout/structured-grading-instructions-assessment-layout.component.ts b/src/main/webapp/app/assessment/structured-grading-instructions-assessment-layout/structured-grading-instructions-assessment-layout.component.ts index 46be8ca706ce..5c2f2e447f45 100644 --- a/src/main/webapp/app/assessment/structured-grading-instructions-assessment-layout/structured-grading-instructions-assessment-layout.component.ts +++ b/src/main/webapp/app/assessment/structured-grading-instructions-assessment-layout/structured-grading-instructions-assessment-layout.component.ts @@ -4,11 +4,17 @@ import { AfterViewInit, Component, Input, OnInit, QueryList, ViewChildren } from import { faCompress, faExpand, faInfoCircle } from '@fortawesome/free-solid-svg-icons'; import { ExpandableSectionComponent } from 'app/assessment/assessment-instructions/expandable-section/expandable-section.component'; import { delay, startWith } from 'rxjs'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-structured-grading-instructions-assessment-layout', templateUrl: './structured-grading-instructions-assessment-layout.component.html', styleUrls: ['./structured-grading-instructions-assessment-layout.component.scss'], + imports: [FaIconComponent, TranslateDirective, ExpandableSectionComponent, NgbTooltip, HelpIconComponent, HtmlForMarkdownPipe], }) export class StructuredGradingInstructionsAssessmentLayoutComponent implements OnInit, AfterViewInit { @Input() public criteria: GradingCriterion[]; diff --git a/src/main/webapp/app/assessment/unreferenced-feedback-detail/assessment-correction-round-badge/assessment-correction-round-badge.component.ts b/src/main/webapp/app/assessment/unreferenced-feedback-detail/assessment-correction-round-badge/assessment-correction-round-badge.component.ts index 63318de8c442..71efaf15d01c 100644 --- a/src/main/webapp/app/assessment/unreferenced-feedback-detail/assessment-correction-round-badge/assessment-correction-round-badge.component.ts +++ b/src/main/webapp/app/assessment/unreferenced-feedback-detail/assessment-correction-round-badge/assessment-correction-round-badge.component.ts @@ -1,10 +1,13 @@ import { Component, Input } from '@angular/core'; import { Feedback } from 'app/entities/feedback.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgStyle } from '@angular/common'; @Component({ selector: 'jhi-assessment-correction-round-badge', templateUrl: './assessment-correction-round-badge.component.html', styleUrls: ['./assessment-correction-round-badge.component.scss'], + imports: [TranslateDirective, NgStyle], }) export class AssessmentCorrectionRoundBadgeComponent { @Input() feedback: Feedback; diff --git a/src/main/webapp/app/assessment/unreferenced-feedback-detail/unreferenced-feedback-detail.component.ts b/src/main/webapp/app/assessment/unreferenced-feedback-detail/unreferenced-feedback-detail.component.ts index 3b4308b752ab..483eebaf28f9 100644 --- a/src/main/webapp/app/assessment/unreferenced-feedback-detail/unreferenced-feedback-detail.component.ts +++ b/src/main/webapp/app/assessment/unreferenced-feedback-detail/unreferenced-feedback-detail.component.ts @@ -5,13 +5,40 @@ import { StructuredGradingCriterionService } from 'app/exercises/shared/structur import { ButtonSize } from 'app/shared/components/button.component'; import { Subject } from 'rxjs'; import { FeedbackService } from 'app/exercises/shared/feedback/feedback.service'; +import { FeedbackSuggestionBadgeComponent } from '../../exercises/shared/feedback/feedback-suggestion-badge/feedback-suggestion-badge.component'; +import { GradingInstructionLinkIconComponent } from 'app/shared/grading-instruction-link-icon/grading-instruction-link-icon.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { FaIconComponent, FaLayersComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { AssessmentCorrectionRoundBadgeComponent } from './assessment-correction-round-badge/assessment-correction-round-badge.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { FeedbackContentPipe } from 'app/shared/pipes/feedback-content.pipe'; +import { QuotePipe } from 'app/shared/pipes/quote.pipe'; @Component({ selector: 'jhi-unreferenced-feedback-detail', templateUrl: './unreferenced-feedback-detail.component.html', styleUrls: ['./unreferenced-feedback-detail.component.scss'], + imports: [ + FeedbackSuggestionBadgeComponent, + GradingInstructionLinkIconComponent, + DeleteButtonDirective, + FaIconComponent, + TranslateDirective, + FormsModule, + NgbTooltip, + FaLayersComponent, + AssessmentCorrectionRoundBadgeComponent, + ArtemisTranslatePipe, + FeedbackContentPipe, + QuotePipe, + ], }) export class UnreferencedFeedbackDetailComponent implements OnInit { + structuredGradingCriterionService = inject(StructuredGradingCriterionService); + @Input() public feedback: Feedback; resultId: InputSignal = input.required(); @Input() isSuggestion: boolean; @@ -40,8 +67,6 @@ export class UnreferencedFeedbackDetailComponent implements OnInit { private dialogErrorSource = new Subject(); dialogError$ = this.dialogErrorSource.asObservable(); - constructor(public structuredGradingCriterionService: StructuredGradingCriterionService) {} - ngOnInit() { this.loadLongFeedback(); } diff --git a/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts b/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts index ed0bef126614..65dca1ca7ad8 100644 --- a/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts +++ b/src/main/webapp/app/complaints/complaints-for-students/complaints-student-view.component.ts @@ -25,7 +25,6 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; selector: 'jhi-complaint-student-view', templateUrl: './complaints-student-view.component.html', styleUrls: ['../complaints.scss'], - standalone: true, imports: [TranslateDirective, FaIconComponent, ComplaintsFormComponent, ComplaintRequestComponent, ComplaintResponseComponent, ArtemisTranslatePipe], }) export class ComplaintsStudentViewComponent implements OnInit { diff --git a/src/main/webapp/app/complaints/complaints-for-tutor/complaints-for-tutor.component.ts b/src/main/webapp/app/complaints/complaints-for-tutor/complaints-for-tutor.component.ts index a98e34d60b17..8478a6080862 100644 --- a/src/main/webapp/app/complaints/complaints-for-tutor/complaints-for-tutor.component.ts +++ b/src/main/webapp/app/complaints/complaints-for-tutor/complaints-for-tutor.component.ts @@ -24,7 +24,6 @@ export type AssessmentAfterComplaint = { complaintResponse: ComplaintResponse; o @Component({ selector: 'jhi-complaints-for-tutor-form', templateUrl: './complaints-for-tutor.component.html', - standalone: true, imports: [TranslateDirective, FormsModule, TextareaModule, ArtemisSharedCommonModule, ArtemisTranslatePipe], }) export class ComplaintsForTutorComponent implements OnInit { diff --git a/src/main/webapp/app/complaints/form/complaints-form.component.ts b/src/main/webapp/app/complaints/form/complaints-form.component.ts index 5d5265a527e1..bcb6725b5fee 100644 --- a/src/main/webapp/app/complaints/form/complaints-form.component.ts +++ b/src/main/webapp/app/complaints/form/complaints-form.component.ts @@ -16,7 +16,6 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; selector: 'jhi-complaint-form', templateUrl: './complaints-form.component.html', styleUrls: ['../complaints.scss'], - standalone: true, imports: [TranslateDirective, FormsModule, TextareaModule, ArtemisTranslatePipe], }) export class ComplaintsFormComponent implements OnInit { diff --git a/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.component.ts b/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.component.ts index a2a3c2197a0b..9f150adfbbe0 100644 --- a/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.component.ts +++ b/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.component.ts @@ -24,7 +24,6 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-complaint-list', templateUrl: './list-of-complaints.component.html', - standalone: true, imports: [TranslateDirective, FormsModule, ArtemisSharedCommonModule, FaIconComponent, NgbTooltip, ArtemisTranslatePipe], }) export class ListOfComplaintsComponent implements OnInit { diff --git a/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.route.ts b/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.route.ts index fbce52b1377d..0058ab0a505c 100644 --- a/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.route.ts +++ b/src/main/webapp/app/complaints/list-of-complaints/list-of-complaints.route.ts @@ -1,6 +1,7 @@ import { Routes } from '@angular/router'; -import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { ListOfComplaintsComponent } from 'app/complaints/list-of-complaints/list-of-complaints.component'; +import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; + import { ComplaintType } from 'app/entities/complaint.model'; import { Authority } from 'app/shared/constants/authority.constants'; import { exerciseTypes } from 'app/entities/exercise.model'; @@ -9,7 +10,7 @@ import { CourseManagementResolve } from 'app/course/manage/course-management-res export const listOfComplaintsRoute: Routes = [ { path: ':courseId/complaints', - component: ListOfComplaintsComponent, + loadComponent: () => import('app/complaints/list-of-complaints/list-of-complaints.component').then((m) => m.ListOfComplaintsComponent), resolve: { course: CourseManagementResolve, }, @@ -22,7 +23,7 @@ export const listOfComplaintsRoute: Routes = [ }, { path: ':courseId/exams/:examId/complaints', - component: ListOfComplaintsComponent, + loadComponent: () => import('app/complaints/list-of-complaints/list-of-complaints.component').then((m) => m.ListOfComplaintsComponent), resolve: { course: CourseManagementResolve, }, @@ -47,7 +48,7 @@ export const listOfComplaintsRoute: Routes = [ }), { path: ':courseId/more-feedback-requests', - component: ListOfComplaintsComponent, + loadComponent: () => import('app/complaints/list-of-complaints/list-of-complaints.component').then((m) => m.ListOfComplaintsComponent), resolve: { course: CourseManagementResolve, }, diff --git a/src/main/webapp/app/complaints/request/complaint-request.component.ts b/src/main/webapp/app/complaints/request/complaint-request.component.ts index 61065c172d4e..205cbe7910c1 100644 --- a/src/main/webapp/app/complaints/request/complaint-request.component.ts +++ b/src/main/webapp/app/complaints/request/complaint-request.component.ts @@ -9,7 +9,6 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-complaint-request', templateUrl: './complaint-request.component.html', - standalone: true, imports: [NgbTooltip, TranslateDirective, FormsModule, ArtemisSharedCommonModule, ArtemisTranslatePipe], }) export class ComplaintRequestComponent { diff --git a/src/main/webapp/app/complaints/response/complaint-response.component.ts b/src/main/webapp/app/complaints/response/complaint-response.component.ts index c2443cf7ea60..159a22226077 100644 --- a/src/main/webapp/app/complaints/response/complaint-response.component.ts +++ b/src/main/webapp/app/complaints/response/complaint-response.component.ts @@ -8,7 +8,6 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-complaint-response', templateUrl: './complaint-response.component.html', - standalone: true, imports: [NgbTooltip, FormsModule, ArtemisSharedCommonModule, ArtemisTranslatePipe], }) export class ComplaintResponseComponent { diff --git a/src/main/webapp/app/core/about-us/about-us.component.ts b/src/main/webapp/app/core/about-us/about-us.component.ts index 4fff2553b602..0c9d9a0c505b 100644 --- a/src/main/webapp/app/core/about-us/about-us.component.ts +++ b/src/main/webapp/app/core/about-us/about-us.component.ts @@ -1,17 +1,24 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { VERSION } from 'app/app.constants'; import { StaticContentService } from 'app/shared/service/static-content.service'; import { AboutUsModel } from 'app/core/about-us/models/about-us-model'; import { ContributorModel } from 'app/core/about-us/models/contributor-model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-about-us', templateUrl: './about-us.component.html', styleUrls: ['./about-us.component.scss'], + imports: [TranslateDirective, ArtemisTranslatePipe], }) export class AboutUsComponent implements OnInit { + private route = inject(ActivatedRoute); + private profileService = inject(ProfileService); + private staticContentService = inject(StaticContentService); + private readonly ISSUE_BASE_URL = 'https://github.com/ls1intum/Artemis/issues/new?projects=ls1intum/1'; readonly BUG_REPORT_URL = `${this.ISSUE_BASE_URL}&labels=bug&template=bug-report.yml`; readonly FEATURE_REQUEST_URL = `${this.ISSUE_BASE_URL}&labels=feature&template=feature-request.yml`; @@ -51,12 +58,6 @@ export class AboutUsComponent implements OnInit { ['openSource', { openSourceUrl: 'https://docs.artemis.cit.tum.de/dev/open-source/' }], ]; - constructor( - private route: ActivatedRoute, - private profileService: ProfileService, - private staticContentService: StaticContentService, - ) {} - /** * On init get the json file from the Artemis server and save it. * On init get the mail data needed for the contact diff --git a/src/main/webapp/app/core/about-us/artemis-about-us.module.ts b/src/main/webapp/app/core/about-us/artemis-about-us.module.ts index 057b2eda4540..f43f418f25f8 100644 --- a/src/main/webapp/app/core/about-us/artemis-about-us.module.ts +++ b/src/main/webapp/app/core/about-us/artemis-about-us.module.ts @@ -1,17 +1,17 @@ import { NgModule } from '@angular/core'; -import { AboutUsComponent } from 'app/core/about-us/about-us.component'; + import { TranslateModule } from '@ngx-translate/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; +import { AboutUsComponent } from 'app/core/about-us/about-us.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; @NgModule({ - declarations: [AboutUsComponent], imports: [ RouterModule.forChild([ { path: '', - component: AboutUsComponent, + loadComponent: () => import('app/core/about-us/about-us.component').then((m) => m.AboutUsComponent), data: { authorities: [], pageTitle: 'overview.aboutUs', @@ -21,6 +21,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; TranslateModule, CommonModule, ArtemisSharedModule, + AboutUsComponent, ], }) export class ArtemisAboutUsModule {} diff --git a/src/main/webapp/app/core/core.module.ts b/src/main/webapp/app/core/core.module.ts deleted file mode 100644 index 30c0442557b2..000000000000 --- a/src/main/webapp/app/core/core.module.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { APP_INITIALIZER, ErrorHandler, LOCALE_ID, NgModule } from '@angular/core'; -import { DatePipe, registerLocaleData } from '@angular/common'; -import { HTTP_INTERCEPTORS, HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; -import { Title } from '@angular/platform-browser'; -import { AuthExpiredInterceptor } from 'app/core/interceptor/auth-expired.interceptor'; -import { ErrorHandlerInterceptor } from 'app/core/interceptor/errorhandler.interceptor'; -import { NotificationInterceptor } from 'app/core/interceptor/notification.interceptor'; -import { NgbDateAdapter, NgbDatepickerConfig, NgbTooltipConfig } from '@ng-bootstrap/ng-bootstrap'; -import { SessionStorageService, provideNgxWebstorage, withLocalStorage, withNgxWebstorageConfig, withSessionStorage } from 'ngx-webstorage'; -import locale from '@angular/common/locales/en'; -import { MissingTranslationHandler, TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; -import { SentryErrorHandler } from 'app/core/sentry/sentry.error-handler'; -import { LoadingNotificationInterceptor } from 'app/shared/notification/loading-notification/loading-notification.interceptor'; -import { BrowserFingerprintInterceptor } from 'app/core/interceptor/browser-fingerprint.interceptor.service'; -import { ArtemisVersionInterceptor, WINDOW_INJECTOR_TOKEN } from 'app/core/interceptor/artemis-version.interceptor'; -import { missingTranslationHandler, translatePartialLoader } from './config/translation.config'; -import dayjs from 'dayjs/esm'; -import './config/dayjs'; -import { NgbDateDayjsAdapter } from 'app/core/config/datepicker-adapter'; -import { JhiLanguageHelper } from 'app/core/language/language.helper'; -import { TraceService } from '@sentry/angular'; -import { Router } from '@angular/router'; -import isMobile from 'ismobilejs-es5'; - -@NgModule({ - imports: [ - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useFactory: translatePartialLoader, - deps: [HttpClient], - }, - missingTranslationHandler: { - provide: MissingTranslationHandler, - useFactory: missingTranslationHandler, - }, - }), - ], - providers: [ - provideHttpClient(withInterceptorsFromDi()), - provideNgxWebstorage(withNgxWebstorageConfig({ prefix: 'jhi', separator: '-' }), withLocalStorage(), withSessionStorage()), - Title, - { - provide: LOCALE_ID, - useValue: 'en', - }, - { provide: NgbDateAdapter, useClass: NgbDateDayjsAdapter }, - { provide: TraceService, deps: [Router] }, - { provide: ErrorHandler, useClass: SentryErrorHandler }, - { provide: WINDOW_INJECTOR_TOKEN, useValue: window }, - DatePipe, - { - provide: APP_INITIALIZER, - useFactory: () => () => {}, - deps: [TraceService], - multi: true, - }, - /** - * @description Interceptor declarations: - * Interceptors are located at 'blocks/interceptor/. - * All of them implement the HttpInterceptor interface. - * They can be used to modify API calls or trigger additional function calls. - * Most interceptors will transform the outgoing request before passing it to - * the next interceptor in the chain, by calling next.handle(transformedReq). - * Documentation: https://angular.io/api/common/http/HttpInterceptor - */ - { - provide: HTTP_INTERCEPTORS, - useClass: AuthExpiredInterceptor, - multi: true, - }, - { - provide: HTTP_INTERCEPTORS, - useClass: ErrorHandlerInterceptor, - multi: true, - }, - { - provide: HTTP_INTERCEPTORS, - useClass: BrowserFingerprintInterceptor, - multi: true, - }, - { - provide: HTTP_INTERCEPTORS, - useClass: NotificationInterceptor, - multi: true, - }, - { - provide: HTTP_INTERCEPTORS, - useClass: LoadingNotificationInterceptor, - multi: true, - }, - { - provide: HTTP_INTERCEPTORS, - useClass: ArtemisVersionInterceptor, - multi: true, - }, - ], -}) -export class ArtemisCoreModule { - constructor( - dpConfig: NgbDatepickerConfig, - tooltipConfig: NgbTooltipConfig, - translateService: TranslateService, - languageHelper: JhiLanguageHelper, - sessionStorageService: SessionStorageService, - ) { - registerLocaleData(locale); - dpConfig.minDate = { year: dayjs().subtract(100, 'year').year(), month: 1, day: 1 }; - translateService.setDefaultLang('en'); - const languageKey = sessionStorageService.retrieve('locale') || languageHelper.determinePreferredLanguage(); - translateService.use(languageKey); - tooltipConfig.container = 'body'; - if (isMobile(window.navigator.userAgent).any ?? false) { - tooltipConfig.disableTooltip = true; - } - } -} diff --git a/src/main/webapp/app/core/interceptor/artemis-version.interceptor.ts b/src/main/webapp/app/core/interceptor/artemis-version.interceptor.ts index e68fa487ec2d..d06badde9302 100644 --- a/src/main/webapp/app/core/interceptor/artemis-version.interceptor.ts +++ b/src/main/webapp/app/core/interceptor/artemis-version.interceptor.ts @@ -1,4 +1,4 @@ -import { ApplicationRef, Inject, Injectable, InjectionToken } from '@angular/core'; +import { ApplicationRef, Injectable, InjectionToken, inject } from '@angular/core'; import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http'; import { Observable, concat, interval, of } from 'rxjs'; import { catchError, first, tap, timeout } from 'rxjs/operators'; @@ -11,18 +11,20 @@ export const WINDOW_INJECTOR_TOKEN = new InjectionToken('Window'); @Injectable() export class ArtemisVersionInterceptor implements HttpInterceptor { + private appRef = inject(ApplicationRef); + private updates = inject(SwUpdate); + private serverDateService = inject(ArtemisServerDateService); + private alertService = inject(AlertService); + private injectedWindow = inject(WINDOW_INJECTOR_TOKEN); + // The currently displayed alert private alert: Alert; // Indicates whether we ever saw an outdated state since last reload private hasSeenOutdatedInThisSession = false; - constructor( - private appRef: ApplicationRef, - private updates: SwUpdate, - private serverDateService: ArtemisServerDateService, - private alertService: AlertService, - @Inject(WINDOW_INJECTOR_TOKEN) private injectedWindow: Window, - ) { + constructor() { + const appRef = this.appRef; + // Allow the app to stabilize first, before starting // polling for updates with `interval()`. const appIsStableOrTimeout = appRef.isStable.pipe( diff --git a/src/main/webapp/app/core/interceptor/auth-expired.interceptor.ts b/src/main/webapp/app/core/interceptor/auth-expired.interceptor.ts index 560b79ecfce6..ef73ff1f2bea 100644 --- a/src/main/webapp/app/core/interceptor/auth-expired.interceptor.ts +++ b/src/main/webapp/app/core/interceptor/auth-expired.interceptor.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @@ -9,12 +9,10 @@ import { AccountService } from 'app/core/auth/account.service'; @Injectable() export class AuthExpiredInterceptor implements HttpInterceptor { - constructor( - private loginService: LoginService, - private stateStorageService: StateStorageService, - private router: Router, - private accountService: AccountService, - ) {} + private loginService = inject(LoginService); + private stateStorageService = inject(StateStorageService); + private router = inject(Router); + private accountService = inject(AccountService); /** * Identifies and handles a given HTTP request. If the request's error status is 401, the current user will be logged out. diff --git a/src/main/webapp/app/core/interceptor/browser-fingerprint.interceptor.service.ts b/src/main/webapp/app/core/interceptor/browser-fingerprint.interceptor.service.ts index d51cca4a20d1..6933636e07ea 100644 --- a/src/main/webapp/app/core/interceptor/browser-fingerprint.interceptor.service.ts +++ b/src/main/webapp/app/core/interceptor/browser-fingerprint.interceptor.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs'; import { BrowserFingerprintService } from 'app/shared/fingerprint/browser-fingerprint.service'; @@ -6,10 +6,14 @@ import { isRequestToArtemisServer } from './interceptor.util'; @Injectable() export class BrowserFingerprintInterceptor implements HttpInterceptor { + private browserFingerprintService = inject(BrowserFingerprintService); + private fingerprint?: string; private instanceIdentifier?: string; - constructor(private browserFingerprintService: BrowserFingerprintService) { + constructor() { + const browserFingerprintService = this.browserFingerprintService; + browserFingerprintService.fingerprint.subscribe((fingerprint) => (this.fingerprint = fingerprint)); browserFingerprintService.instanceIdentifier.subscribe((instanceIdentifier) => (this.instanceIdentifier = instanceIdentifier)); } diff --git a/src/main/webapp/app/core/interceptor/errorhandler.interceptor.ts b/src/main/webapp/app/core/interceptor/errorhandler.interceptor.ts index 6b3b3ce0b9c5..8a3d009fd985 100644 --- a/src/main/webapp/app/core/interceptor/errorhandler.interceptor.ts +++ b/src/main/webapp/app/core/interceptor/errorhandler.interceptor.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @@ -7,10 +7,8 @@ import { AccountService } from 'app/core/auth/account.service'; @Injectable() export class ErrorHandlerInterceptor implements HttpInterceptor { - constructor( - private eventManager: EventManager, - private accountService: AccountService, - ) {} + private eventManager = inject(EventManager); + private accountService = inject(AccountService); /** * Identifies and handles a given HTTP request. If the request's error status is not 401 while the user is not diff --git a/src/main/webapp/app/core/interceptor/notification.interceptor.ts b/src/main/webapp/app/core/interceptor/notification.interceptor.ts index 60cbd80bf09a..e39212e9a8d9 100644 --- a/src/main/webapp/app/core/interceptor/notification.interceptor.ts +++ b/src/main/webapp/app/core/interceptor/notification.interceptor.ts @@ -1,12 +1,12 @@ import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; import { AlertService } from 'app/core/util/alert.service'; @Injectable() export class NotificationInterceptor implements HttpInterceptor { - constructor(private alertService: AlertService) {} + private alertService = inject(AlertService); /** * Identifies and handles a given HTTP request. If the event is a HttpResponse and contains an alert, the alert diff --git a/src/main/webapp/app/core/language/language.helper.ts b/src/main/webapp/app/core/language/language.helper.ts index 60cfe80b69c2..5d5673c77263 100644 --- a/src/main/webapp/app/core/language/language.helper.ts +++ b/src/main/webapp/app/core/language/language.helper.ts @@ -1,4 +1,4 @@ -import { Injectable, Renderer2, RendererFactory2 } from '@angular/core'; +import { Injectable, Renderer2, RendererFactory2, inject } from '@angular/core'; import { Title } from '@angular/platform-browser'; import { ActivatedRouteSnapshot, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; @@ -11,17 +11,18 @@ import { LocaleConversionService } from 'app/shared/service/locale-conversion.se @Injectable({ providedIn: 'root' }) export class JhiLanguageHelper { + private translateService = inject(TranslateService); + private localeConversionService = inject(LocaleConversionService); + private titleService = inject(Title); + private router = inject(Router); + private sessionStorage = inject(SessionStorageService); + private renderer: Renderer2; private _language: BehaviorSubject; - constructor( - private translateService: TranslateService, - private localeConversionService: LocaleConversionService, - private titleService: Title, - private router: Router, - rootRenderer: RendererFactory2, - private sessionStorage: SessionStorageService, - ) { + constructor() { + const rootRenderer = inject(RendererFactory2); + this._language = new BehaviorSubject(this.translateService.currentLang); this.renderer = rootRenderer.createRenderer(document.querySelector('html'), null); this.init(); diff --git a/src/main/webapp/app/core/legal/data-export/confirmation/data-export-confirmation-dialog.component.ts b/src/main/webapp/app/core/legal/data-export/confirmation/data-export-confirmation-dialog.component.ts index 9887df9239e3..aaa457e0927e 100644 --- a/src/main/webapp/app/core/legal/data-export/confirmation/data-export-confirmation-dialog.component.ts +++ b/src/main/webapp/app/core/legal/data-export/confirmation/data-export-confirmation-dialog.component.ts @@ -1,15 +1,23 @@ -import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; +import { Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild, inject } from '@angular/core'; import { Observable, Subscription } from 'rxjs'; import { AlertService } from 'app/core/util/alert.service'; -import { NgForm } from '@angular/forms'; +import { FormsModule, NgForm } from '@angular/forms'; import { faBan, faCheck, faSpinner } from '@fortawesome/free-solid-svg-icons'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TypeAheadUserSearchFieldComponent } from 'app/shared/type-ahead-search-field/type-ahead-user-search-field.component'; +import { ConfirmEntityNameComponent } from 'app/shared/confirm-entity-name/confirm-entity-name.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-data-export-confirmation-dialog', templateUrl: './data-export-confirmation-dialog.component.html', + imports: [FormsModule, TranslateDirective, TypeAheadUserSearchFieldComponent, ConfirmEntityNameComponent, FaIconComponent], }) export class DataExportConfirmationDialogComponent implements OnInit, OnDestroy { + private activeModal = inject(NgbActiveModal); + private alertService = inject(AlertService); + private dialogErrorSubscription: Subscription; dialogError: Observable; @Output() dataExportRequest: EventEmitter; @@ -31,11 +39,6 @@ export class DataExportConfirmationDialogComponent implements OnInit, OnDestroy faSpinner = faSpinner; faCheck = faCheck; - constructor( - private activeModal: NgbActiveModal, - private alertService: AlertService, - ) {} - /** * Life cycle hook called by Angular to indicate that Angular is done creating the component */ diff --git a/src/main/webapp/app/core/legal/data-export/confirmation/data-export-confirmation-dialog.service.ts b/src/main/webapp/app/core/legal/data-export/confirmation/data-export-confirmation-dialog.service.ts index f4d24980e435..ef6f6ea01b2f 100644 --- a/src/main/webapp/app/core/legal/data-export/confirmation/data-export-confirmation-dialog.service.ts +++ b/src/main/webapp/app/core/legal/data-export/confirmation/data-export-confirmation-dialog.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { from } from 'rxjs'; import { AlertService } from 'app/core/util/alert.service'; @@ -7,12 +7,10 @@ import { DataExportConfirmationDialogComponent } from 'app/core/legal/data-expor @Injectable({ providedIn: 'root' }) export class DataExportConfirmationDialogService { - modalRef: NgbModalRef | null; + private modalService = inject(NgbModal); + alertService = inject(AlertService); - constructor( - private modalService: NgbModal, - public alertService: AlertService, - ) {} + modalRef: NgbModalRef | null; /** * Opens data export confirmation dialog diff --git a/src/main/webapp/app/core/legal/data-export/confirmation/data-export-request-button.directive.ts b/src/main/webapp/app/core/legal/data-export/confirmation/data-export-request-button.directive.ts index 9a56872cde64..f2134afd8bb7 100644 --- a/src/main/webapp/app/core/legal/data-export/confirmation/data-export-request-button.directive.ts +++ b/src/main/webapp/app/core/legal/data-export/confirmation/data-export-request-button.directive.ts @@ -1,4 +1,4 @@ -import { Directive, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, Renderer2 } from '@angular/core'; +import { Directive, ElementRef, EventEmitter, HostListener, Input, OnInit, Output, Renderer2, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Observable } from 'rxjs'; import { DataExportConfirmationDialogData } from 'app/core/legal/data-export/confirmation/data-export-confirmation-dialog.model'; @@ -6,6 +6,11 @@ import { DataExportConfirmationDialogService } from 'app/core/legal/data-export/ @Directive({ selector: '[jhiDataExportRequestButton]' }) export class DataExportRequestButtonDirective implements OnInit { + private dataExportConfirmationDialogService = inject(DataExportConfirmationDialogService); + private renderer = inject(Renderer2); + private elementRef = inject(ElementRef); + private translateService = inject(TranslateService); + @Input() expectedLogin: string; @Input() dialogError: Observable; @Input() adminDialog = false; @@ -13,13 +18,6 @@ export class DataExportRequestButtonDirective implements OnInit { @Output() dataExportRequestForAnotherUser = new EventEmitter(); private buttonText: HTMLElement; - constructor( - private dataExportConfirmationDialogService: DataExportConfirmationDialogService, - private renderer: Renderer2, - private elementRef: ElementRef, - private translateService: TranslateService, - ) {} - /** * This method appends classes and type property to the button on which directive was used, additionally adds a span tag with delete text. * We can't use component, as Angular would wrap it in its own tag and this will break button grouping that we are using for other buttons. diff --git a/src/main/webapp/app/core/legal/data-export/data-export.component.ts b/src/main/webapp/app/core/legal/data-export/data-export.component.ts index 37b5de87c4e7..d402dc621668 100644 --- a/src/main/webapp/app/core/legal/data-export/data-export.component.ts +++ b/src/main/webapp/app/core/legal/data-export/data-export.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActionType } from 'app/shared/delete-dialog/delete-dialog.model'; import { Subject } from 'rxjs'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; @@ -9,12 +9,23 @@ import { AlertService } from 'app/core/util/alert.service'; import { DataExport, DataExportState } from 'app/entities/data-export.model'; import { ActivatedRoute } from '@angular/router'; import { convertDateFromServer } from 'app/utils/date.utils'; +import { DataExportRequestButtonDirective } from './confirmation/data-export-request-button.directive'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-data-export', templateUrl: './data-export.component.html', + imports: [DataExportRequestButtonDirective, ButtonComponent, TranslateDirective, ArtemisDatePipe, ArtemisTranslatePipe], }) export class DataExportComponent implements OnInit { + private dataExportService = inject(DataExportService); + private accountService = inject(AccountService); + private alertService = inject(AlertService); + private route = inject(ActivatedRoute); + readonly ActionType = ActionType; readonly ButtonSize = ButtonSize; readonly ButtonType = ButtonType; @@ -34,13 +45,6 @@ export class DataExportComponent implements OnInit { dataExport: DataExport = new DataExport(); isAdmin = false; - constructor( - private dataExportService: DataExportService, - private accountService: AccountService, - private alertService: AlertService, - private route: ActivatedRoute, - ) {} - ngOnInit() { this.currentLogin = this.accountService.userIdentity?.login; this.isAdmin = this.accountService.isAdmin(); diff --git a/src/main/webapp/app/core/legal/data-export/data-export.service.ts b/src/main/webapp/app/core/legal/data-export/data-export.service.ts index f7b2ce5a8527..87296c4d7cd5 100644 --- a/src/main/webapp/app/core/legal/data-export/data-export.service.ts +++ b/src/main/webapp/app/core/legal/data-export/data-export.service.ts @@ -1,11 +1,11 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { DataExport } from 'app/entities/data-export.model'; @Injectable({ providedIn: 'root' }) export class DataExportService { - constructor(private http: HttpClient) {} + private http = inject(HttpClient); requestDataExport(): Observable { return this.http.post(`api/data-exports`, {}); diff --git a/src/main/webapp/app/core/legal/imprint-routing.module.ts b/src/main/webapp/app/core/legal/imprint-routing.module.ts index 08ec21c8f666..9facac88ab95 100644 --- a/src/main/webapp/app/core/legal/imprint-routing.module.ts +++ b/src/main/webapp/app/core/legal/imprint-routing.module.ts @@ -1,8 +1,6 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { ImprintComponent } from 'app/core/legal/imprint.component'; - const routes: Routes = [ { path: '', @@ -10,7 +8,7 @@ const routes: Routes = [ { path: '', pathMatch: 'full', - component: ImprintComponent, + loadComponent: () => import('app/core/legal/imprint.component').then((m) => m.ImprintComponent), data: { authorities: [], pageTitle: 'artemisApp.legal.imprint.title', @@ -18,7 +16,7 @@ const routes: Routes = [ }, { path: ':fragment', - component: ImprintComponent, + loadComponent: () => import('app/core/legal/imprint.component').then((m) => m.ImprintComponent), data: { authorities: [], pageTitle: 'artemisApp.legal.imprint.title', diff --git a/src/main/webapp/app/core/legal/imprint.component.ts b/src/main/webapp/app/core/legal/imprint.component.ts index c7b1ca5e2ff6..d86d46451247 100644 --- a/src/main/webapp/app/core/legal/imprint.component.ts +++ b/src/main/webapp/app/core/legal/imprint.component.ts @@ -1,24 +1,24 @@ -import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; +import { AfterViewInit, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs'; import { JhiLanguageHelper } from 'app/core/language/language.helper'; import { LegalDocumentLanguage } from 'app/entities/legal-document.model'; import { LegalDocumentService } from 'app/shared/service/legal-document.service'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-imprint', template: `
    `, + imports: [HtmlForMarkdownPipe], }) export class ImprintComponent implements AfterViewInit, OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private legalDocumentService = inject(LegalDocumentService); + private languageHelper = inject(JhiLanguageHelper); + imprint?: string; private languageChangeSubscription?: Subscription; - constructor( - private route: ActivatedRoute, - private legalDocumentService: LegalDocumentService, - private languageHelper: JhiLanguageHelper, - ) {} - /** * On init get the Imprint statement file from the Artemis server and set up a subscription to fetch the file again if the language was changed. */ diff --git a/src/main/webapp/app/core/legal/imprint.module.ts b/src/main/webapp/app/core/legal/imprint.module.ts index 867b3209bdbb..d731771c22fd 100644 --- a/src/main/webapp/app/core/legal/imprint.module.ts +++ b/src/main/webapp/app/core/legal/imprint.module.ts @@ -7,7 +7,6 @@ import { ImprintComponent } from 'app/core/legal/imprint.component'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; @NgModule({ - declarations: [ImprintComponent], - imports: [CommonModule, ArtemisSharedModule, ImprintRoutingModule, ArtemisMarkdownModule], + imports: [CommonModule, ArtemisSharedModule, ImprintRoutingModule, ArtemisMarkdownModule, ImprintComponent], }) export class ArtemisImprintModule {} diff --git a/src/main/webapp/app/core/legal/privacy-routing.module.ts b/src/main/webapp/app/core/legal/privacy-routing.module.ts index 4ea113f257b1..cae0efb8ebd4 100644 --- a/src/main/webapp/app/core/legal/privacy-routing.module.ts +++ b/src/main/webapp/app/core/legal/privacy-routing.module.ts @@ -1,15 +1,13 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { PrivacyComponent } from 'app/core/legal/privacy.component'; -import { DataExportComponent } from 'app/core/legal/data-export/data-export.component'; import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; const routes: Routes = [ { path: 'data-exports/:id', - component: DataExportComponent, + loadComponent: () => import('app/core/legal/data-export/data-export.component').then((m) => m.DataExportComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.dataExport.title', @@ -18,7 +16,7 @@ const routes: Routes = [ }, { path: 'data-exports', - component: DataExportComponent, + loadComponent: () => import('app/core/legal/data-export/data-export.component').then((m) => m.DataExportComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.dataExport.title', @@ -31,7 +29,7 @@ const routes: Routes = [ { path: '', pathMatch: 'full', - component: PrivacyComponent, + loadComponent: () => import('app/core/legal/privacy.component').then((m) => m.PrivacyComponent), data: { authorities: [], pageTitle: 'artemisApp.legal.privacyStatement.title', @@ -39,7 +37,7 @@ const routes: Routes = [ }, { path: ':fragment', - component: PrivacyComponent, + loadComponent: () => import('app/core/legal/privacy.component').then((m) => m.PrivacyComponent), data: { authorities: [], pageTitle: 'artemisApp.legal.privacyStatement.title', diff --git a/src/main/webapp/app/core/legal/privacy.component.ts b/src/main/webapp/app/core/legal/privacy.component.ts index 95a9b73eec9c..c7be0da1f787 100644 --- a/src/main/webapp/app/core/legal/privacy.component.ts +++ b/src/main/webapp/app/core/legal/privacy.component.ts @@ -1,10 +1,12 @@ -import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { AfterViewInit, Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { Subscription } from 'rxjs'; import { JhiLanguageHelper } from 'app/core/language/language.helper'; import { AccountService } from 'app/core/auth/account.service'; import { LegalDocumentService } from 'app/shared/service/legal-document.service'; import { LegalDocumentLanguage } from 'app/entities/legal-document.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-privacy', @@ -14,19 +16,18 @@ import { LegalDocumentLanguage } from 'app/entities/legal-document.model'; } `, + imports: [TranslateDirective, RouterLink, HtmlForMarkdownPipe], }) export class PrivacyComponent implements AfterViewInit, OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private legalDocumentService = inject(LegalDocumentService); + private languageHelper = inject(JhiLanguageHelper); + private accountService = inject(AccountService); + privacyStatement?: string; private languageChangeSubscription?: Subscription; isAuthenticated: boolean; - constructor( - private route: ActivatedRoute, - private legalDocumentService: LegalDocumentService, - private languageHelper: JhiLanguageHelper, - private accountService: AccountService, - ) {} - /** * On init get the privacy statement file from the Artemis server and set up a subscription to fetch the file again if the language was changed. */ diff --git a/src/main/webapp/app/core/legal/privacy.module.ts b/src/main/webapp/app/core/legal/privacy.module.ts index 54f04bfe99df..2a7ac518f8c8 100644 --- a/src/main/webapp/app/core/legal/privacy.module.ts +++ b/src/main/webapp/app/core/legal/privacy.module.ts @@ -12,7 +12,17 @@ import { DataExportRequestButtonDirective } from 'app/core/legal/data-export/con import { TypeAheadUserSearchFieldModule } from 'app/shared/type-ahead-search-field/type-ahead-user-search-field.module'; @NgModule({ - declarations: [PrivacyComponent, DataExportComponent, DataExportConfirmationDialogComponent, DataExportRequestButtonDirective], - imports: [CommonModule, ArtemisSharedComponentModule, ArtemisSharedModule, PrivacyRoutingModule, ArtemisMarkdownModule, TypeAheadUserSearchFieldModule], + imports: [ + CommonModule, + ArtemisSharedComponentModule, + ArtemisSharedModule, + PrivacyRoutingModule, + ArtemisMarkdownModule, + TypeAheadUserSearchFieldModule, + PrivacyComponent, + DataExportComponent, + DataExportConfirmationDialogComponent, + DataExportRequestButtonDirective, + ], }) export class ArtemisPrivacyModule {} diff --git a/src/main/webapp/app/core/sentry/sentry.error-handler.ts b/src/main/webapp/app/core/sentry/sentry.error-handler.ts index be90f524d878..da452aee779d 100644 --- a/src/main/webapp/app/core/sentry/sentry.error-handler.ts +++ b/src/main/webapp/app/core/sentry/sentry.error-handler.ts @@ -35,10 +35,6 @@ export class SentryErrorHandler extends ErrorHandler { }); } - constructor() { - super(); - } - /** * Send an HttpError to Sentry. Only if it's not in the range 400-499. * @param error diff --git a/src/main/webapp/app/core/theme/theme-switch.component.ts b/src/main/webapp/app/core/theme/theme-switch.component.ts index 2108bd3a0fb6..29c20a61a52b 100644 --- a/src/main/webapp/app/core/theme/theme-switch.component.ts +++ b/src/main/webapp/app/core/theme/theme-switch.component.ts @@ -20,7 +20,6 @@ import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; styleUrls: ['theme-switch.component.scss'], imports: [TranslateModule, CommonModule, ArtemisSharedModule, NgbModule, FontAwesomeModule], changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, }) export class ThemeSwitchComponent implements OnInit { protected readonly faSync = faSync; diff --git a/src/main/webapp/app/core/user/admin-user.service.ts b/src/main/webapp/app/core/user/admin-user.service.ts index 73e14d154597..a3d0efd1d496 100644 --- a/src/main/webapp/app/core/user/admin-user.service.ts +++ b/src/main/webapp/app/core/user/admin-user.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; @@ -8,9 +8,9 @@ import { UserFilter } from 'app/admin/user-management/user-management.component' @Injectable({ providedIn: 'root' }) export class AdminUserService { - public resourceUrl = 'api/admin/users'; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + public resourceUrl = 'api/admin/users'; /** * Create a user on the server. diff --git a/src/main/webapp/app/core/user/user.service.ts b/src/main/webapp/app/core/user/user.service.ts index 7544c8525fe0..63c7fea088b0 100644 --- a/src/main/webapp/app/core/user/user.service.ts +++ b/src/main/webapp/app/core/user/user.service.ts @@ -1,13 +1,13 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { User } from 'app/core/user/user.model'; @Injectable({ providedIn: 'root' }) export class UserService { - public resourceUrl = 'api/users'; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + public resourceUrl = 'api/users'; /** * Search for a user on the server by login or name. diff --git a/src/main/webapp/app/core/util/alert.service.ts b/src/main/webapp/app/core/util/alert.service.ts index a2052b623c6c..dbc534b0efdb 100644 --- a/src/main/webapp/app/core/util/alert.service.ts +++ b/src/main/webapp/app/core/util/alert.service.ts @@ -1,4 +1,4 @@ -import { Injectable, NgZone, SecurityContext } from '@angular/core'; +import { Injectable, NgZone, SecurityContext, inject } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; import { TranslateService } from '@ngx-translate/core'; import { translationNotFoundMessage } from 'app/core/config/translation.config'; @@ -57,6 +57,10 @@ const DEFAULT_DISMISSIBLE = true; providedIn: 'root', }) export class AlertService { + private sanitizer = inject(DomSanitizer); + private ngZone = inject(NgZone); + private translateService = inject(TranslateService); + private alerts: AlertInternal[] = []; errorListener: Subscription; @@ -64,12 +68,9 @@ export class AlertService { readonly conflictErrorKeysToSkip: string[] = ['cannotRegisterInstructor']; - constructor( - private sanitizer: DomSanitizer, - private ngZone: NgZone, - private translateService: TranslateService, - eventManager: EventManager, - ) { + constructor() { + const eventManager = inject(EventManager); + this.errorListener = eventManager.subscribe('artemisApp.error', (response: EventWithContent | string) => { const errorResponse = (response as EventWithContent).content; this.addErrorAlert(errorResponse.message, errorResponse.translationKey, errorResponse.translationParams); @@ -97,8 +98,8 @@ export class AlertService { entityKey = httpErrorResponse.headers.get(entry); } }); - if (errorHeader && !translateService.instant(errorHeader).startsWith(translationNotFoundMessage)) { - const entityName = translateService.instant('global.menu.entities.' + entityKey); + if (errorHeader && !this.translateService.instant(errorHeader).startsWith(translationNotFoundMessage)) { + const entityName = this.translateService.instant('global.menu.entities.' + entityKey); this.addErrorAlert(errorHeader, errorHeader, { entityName, ...httpErrorResponse.error?.params }); } else if (httpErrorResponse.error && httpErrorResponse.error.fieldErrors) { const fieldErrors = httpErrorResponse.error.fieldErrors; @@ -110,7 +111,7 @@ export class AlertService { } // convert 'something[14].other[4].id' to 'something[].other[].id' so translations can be written to it const convertedField = fieldError.field.replace(/\[\d*\]/g, '[]'); - const fieldName = translateService.instant('artemisApp.' + fieldError.objectName + '.' + convertedField); + const fieldName = this.translateService.instant('artemisApp.' + fieldError.objectName + '.' + convertedField); this.addErrorAlert('Error on field "' + fieldName + '"', 'error.' + fieldError.message, { fieldName }); } } else if (httpErrorResponse.error && httpErrorResponse.error.title) { diff --git a/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.ts b/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.ts index bdc5747b4ed3..2ef00f2d3588 100644 --- a/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.ts +++ b/src/main/webapp/app/course/competencies/competencies-popover/competencies-popover.component.ts @@ -1,12 +1,17 @@ import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'; import { faFlag } from '@fortawesome/free-solid-svg-icons'; import { CompetencyLectureUnitLink } from 'app/entities/competency.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { RouterLink } from '@angular/router'; +import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-competencies-popover', templateUrl: './competencies-popover.component.html', styleUrls: ['./competencies-popover.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [TranslateDirective, RouterLink, NgbPopover, FaIconComponent], }) export class CompetenciesPopoverComponent implements OnInit { @Input() @@ -21,8 +26,6 @@ export class CompetenciesPopoverComponent implements OnInit { // Icons faFlag = faFlag; - constructor() {} - ngOnInit(): void { if (this.courseId) { switch (this.navigateTo) { diff --git a/src/main/webapp/app/course/competencies/competency-accordion/competency-accordion.component.ts b/src/main/webapp/app/course/competencies/competency-accordion/competency-accordion.component.ts index 51fd9e4ae16c..0049a4e749ee 100644 --- a/src/main/webapp/app/course/competencies/competency-accordion/competency-accordion.component.ts +++ b/src/main/webapp/app/course/competencies/competency-accordion/competency-accordion.component.ts @@ -1,15 +1,22 @@ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, inject } from '@angular/core'; import { faFile, faFilePdf, faList } from '@fortawesome/free-solid-svg-icons'; import { MIN_SCORE_GREEN } from 'app/app.constants'; import { Competency, CompetencyJol, CompetencyProgress, getConfidence, getIcon, getMastery, getProgress } from 'app/entities/competency.model'; import { Course } from 'app/entities/course.model'; -import { Router } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; import { CompetencyInformation, LectureUnitInformation, StudentMetrics } from 'app/entities/student-metrics.model'; import { round } from 'app/shared/util/utils'; import { Exercise } from 'app/entities/exercise.model'; import dayjs from 'dayjs/esm'; import { LectureUnitType, lectureUnitIcons, lectureUnitTooltips } from 'app/entities/lecture-unit/lectureUnit.model'; import { isStartPracticeAvailable } from 'app/exercises/shared/exercise/exercise.utils'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbProgressbar, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { CompetencyRingsComponent } from '../competency-rings/competency-rings.component'; +import { JudgementOfLearningRatingComponent } from '../judgement-of-learning-rating/judgement-of-learning-rating.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CourseExerciseRowComponent } from 'app/overview/course-exercises/course-exercise-row.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export interface CompetencyAccordionToggleEvent { opened: boolean; @@ -20,8 +27,21 @@ export interface CompetencyAccordionToggleEvent { selector: 'jhi-competency-accordion', templateUrl: './competency-accordion.component.html', styleUrl: './competency-accordion.component.scss', + imports: [ + FaIconComponent, + NgbTooltip, + NgbProgressbar, + CompetencyRingsComponent, + JudgementOfLearningRatingComponent, + TranslateDirective, + CourseExerciseRowComponent, + RouterLink, + ArtemisTranslatePipe, + ], }) export class CompetencyAccordionComponent implements OnChanges { + private router = inject(Router); + @Input() course: Course | undefined; @Input() competency: CompetencyInformation; @Input() metrics: StudentMetrics; @@ -53,8 +73,6 @@ export class CompetencyAccordionComponent implements OnChanges { protected readonly getMastery = getMastery; protected readonly round = round; - constructor(private router: Router) {} - ngOnChanges(changes: SimpleChanges) { if (changes.openedIndex && this.index !== this.openedIndex) { this.open = false; diff --git a/src/main/webapp/app/course/competencies/competency-card/competency-card.component.ts b/src/main/webapp/app/course/competencies/competency-card/competency-card.component.ts index 462369931d12..6a3293bd12af 100644 --- a/src/main/webapp/app/course/competencies/competency-card/competency-card.component.ts +++ b/src/main/webapp/app/course/competencies/competency-card/competency-card.component.ts @@ -1,14 +1,26 @@ import dayjs from 'dayjs/esm'; -import { Component, input } from '@angular/core'; +import { Component, inject, input } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { CompetencyProgress, CourseCompetency, getIcon, getMastery, getProgress } from 'app/entities/competency.model'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass } from '@angular/common'; +import { CompetencyRingsComponent } from '../competency-rings/competency-rings.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-competency-card', templateUrl: './competency-card.component.html', styleUrls: ['../../../overview/course-exercises/course-exercise-row.scss'], + imports: [RouterLink, FaIconComponent, NgbTooltip, TranslateDirective, NgClass, CompetencyRingsComponent, ArtemisTranslatePipe, ArtemisTimeAgoPipe, HtmlForMarkdownPipe], }) export class CompetencyCardComponent { + translateService = inject(TranslateService); + courseId = input(); competency = input(); isPrerequisite = input(); @@ -17,8 +29,6 @@ export class CompetencyCardComponent { protected readonly getIcon = getIcon; - constructor(public translateService: TranslateService) {} - getUserProgress(): CompetencyProgress { const userProgress = this.competency()?.userProgress?.first(); if (userProgress) { diff --git a/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts b/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts index 7d939bdf5c67..ba3cc6666a17 100644 --- a/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts +++ b/src/main/webapp/app/course/competencies/competency-management/competency-management-table.component.ts @@ -7,7 +7,7 @@ import { filter, map } from 'rxjs/operators'; import { onError } from 'app/shared/util/global.utils'; import { Subject } from 'rxjs'; import { faFileImport, faPencilAlt, faPlus, faTrash } from '@fortawesome/free-solid-svg-icons'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbDropdown, NgbDropdownMenu, NgbDropdownToggle, NgbModal, NgbProgressbar } from '@ng-bootstrap/ng-bootstrap'; import { ImportAllCompetenciesComponent, ImportAllFromCourseResult } from 'app/course/competencies/competency-management/import-all-competencies.component'; import { PrerequisiteService } from 'app/course/competencies/prerequisite.service'; import { ArtemisSharedModule } from 'app/shared/shared.module'; @@ -16,16 +16,16 @@ import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; @Component({ selector: 'jhi-competency-management-table', templateUrl: './competency-management-table.component.html', - standalone: true, - imports: [ArtemisSharedModule, ArtemisMarkdownModule], + imports: [ArtemisSharedModule, ArtemisMarkdownModule, NgbProgressbar, NgbDropdown, NgbDropdownMenu, NgbDropdownToggle], }) export class CompetencyManagementTableComponent implements OnInit, OnDestroy { @Input() courseId: number; @Input() courseCompetencies: CourseCompetency[] = []; - allCompetencies = model.required(); @Input() competencyType: CourseCompetencyType; @Input() standardizedCompetenciesEnabled: boolean; + allCompetencies = model.required(); + @Output() competencyDeleted = new EventEmitter(); service: CompetencyService | PrerequisiteService; diff --git a/src/main/webapp/app/course/competencies/competency-management/competency-management.component.ts b/src/main/webapp/app/course/competencies/competency-management/competency-management.component.ts index c0c3c3936f71..2b80ae142184 100644 --- a/src/main/webapp/app/course/competencies/competency-management/competency-management.component.ts +++ b/src/main/webapp/app/course/competencies/competency-management/competency-management.component.ts @@ -26,7 +26,6 @@ import { toSignal } from '@angular/core/rxjs-interop'; @Component({ selector: 'jhi-competency-management', templateUrl: './competency-management.component.html', - standalone: true, imports: [CompetencyManagementTableComponent, TranslateDirective, FontAwesomeModule, RouterModule, ArtemisSharedComponentModule], }) export class CompetencyManagementComponent implements OnInit { diff --git a/src/main/webapp/app/course/competencies/competency-management/import-all-competencies.component.ts b/src/main/webapp/app/course/competencies/competency-management/import-all-competencies.component.ts index 9774c03a1bbe..ead28d847b8f 100644 --- a/src/main/webapp/app/course/competencies/competency-management/import-all-competencies.component.ts +++ b/src/main/webapp/app/course/competencies/competency-management/import-all-competencies.component.ts @@ -1,13 +1,11 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { Course, CourseForImportDTO } from 'app/entities/course.model'; -import { SortService } from 'app/shared/service/sort.service'; -import { Router } from '@angular/router'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { CourseForImportDTOPagingService } from 'app/course/course-for-import-dto-paging-service'; import { Column, ImportComponent } from 'app/shared/import/import.component'; import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { CourseCompetencyType } from 'app/entities/competency.model'; +import { NgbPagination } from '@ng-bootstrap/ng-bootstrap'; +import { CourseForImportDTOPagingService } from 'app/course/course-for-import-dto-paging-service'; const tableColumns: Column[] = [ { @@ -38,8 +36,7 @@ export type ImportAllFromCourseResult = { @Component({ selector: 'jhi-import-all-competencies', templateUrl: './import-all-competencies.component.html', - standalone: true, - imports: [ArtemisSharedCommonModule, ArtemisSharedComponentModule], + imports: [ArtemisSharedCommonModule, ArtemisSharedComponentModule, NgbPagination], }) export class ImportAllCompetenciesComponent extends ImportComponent { //import relations by default @@ -47,8 +44,9 @@ export class ImportAllCompetenciesComponent extends ImportComponent; @@ -15,10 +12,6 @@ type EntityArrayResponseType = HttpResponse; providedIn: 'root', }) export class CompetencyService extends CourseCompetencyService { - constructor(httpClient: HttpClient, entityTitleService: EntityTitleService, lectureUnitService: LectureUnitService, accountService: AccountService) { - super(httpClient, entityTitleService, lectureUnitService, accountService); - } - getAllForCourse(courseId: number): Observable { return this.httpClient.get(`${this.resourceURL}/courses/${courseId}/competencies`, { observe: 'response' }).pipe( map((res: EntityArrayResponseType) => this.convertArrayResponseDatesFromServer(res)), diff --git a/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.ts b/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.ts index 448d2e4b1d77..1c247e9d1f15 100644 --- a/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.ts +++ b/src/main/webapp/app/course/competencies/components/course-competencies-relation-graph/course-competencies-relation-graph.component.ts @@ -11,7 +11,6 @@ import { CourseCompetencyRelationNodeComponent } from 'app/course/competencies/c @Component({ selector: 'jhi-course-competencies-relation-graph', - standalone: true, imports: [FontAwesomeModule, NgbAccordionModule, NgxGraphModule, ArtemisSharedModule, CourseCompetencyRelationNodeComponent], templateUrl: './course-competencies-relation-graph.component.html', styleUrl: './course-competencies-relation-graph.component.scss', @@ -44,23 +43,20 @@ export class CourseCompetenciesRelationGraphComponent { }); constructor() { - effect( - () => { - return this.nodes.set( - this.courseCompetencies().map( - (courseCompetency): Node => ({ - id: courseCompetency.id!.toString(), - label: courseCompetency.title, - data: { - id: courseCompetency.id, - type: courseCompetency.type, - }, - }), - ), - ); - }, - { allowSignalWrites: true }, - ); + effect(() => { + return this.nodes.set( + this.courseCompetencies().map( + (courseCompetency): Node => ({ + id: courseCompetency.id!.toString(), + label: courseCompetency.title, + data: { + id: courseCompetency.id, + type: courseCompetency.type, + }, + }), + ), + ); + }); } protected selectRelation(relationId: number): void { diff --git a/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.ts b/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.ts index df0547c57a44..ac2a68f7a312 100644 --- a/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.ts +++ b/src/main/webapp/app/course/competencies/components/course-competencies-relation-modal/course-competencies-relation-modal.component.ts @@ -4,15 +4,13 @@ import { CompetencyRelationDTO, CourseCompetency } from 'app/entities/competency import { AlertService } from 'app/core/util/alert.service'; import { onError } from 'app/shared/util/global.utils'; import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; -import { CompetencyGraphComponent } from 'app/course/learning-paths/components/competency-graph/competency-graph.component'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { CourseCompetencyRelationFormComponent } from 'app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component'; import { CourseCompetenciesRelationGraphComponent } from '../course-competencies-relation-graph/course-competencies-relation-graph.component'; @Component({ selector: 'jhi-course-competencies-relation-modal', - standalone: true, - imports: [ArtemisSharedCommonModule, CompetencyGraphComponent, CourseCompetenciesRelationGraphComponent, CourseCompetencyRelationFormComponent], + imports: [ArtemisSharedCommonModule, CourseCompetenciesRelationGraphComponent, CourseCompetencyRelationFormComponent], templateUrl: './course-competencies-relation-modal.component.html', styleUrl: './course-competencies-relation-modal.component.scss', }) @@ -32,7 +30,7 @@ export class CourseCompetenciesRelationModalComponent { readonly relations = signal([]); constructor() { - effect(() => this.loadRelations(this.courseId()), { allowSignalWrites: true }); + effect(() => this.loadRelations(this.courseId())); } private async loadRelations(courseId: number): Promise { diff --git a/src/main/webapp/app/course/competencies/components/course-competency-explanation-modal/course-competency-explanation-modal.component.ts b/src/main/webapp/app/course/competencies/components/course-competency-explanation-modal/course-competency-explanation-modal.component.ts index 8cd333d439f3..44176e8f570f 100644 --- a/src/main/webapp/app/course/competencies/components/course-competency-explanation-modal/course-competency-explanation-modal.component.ts +++ b/src/main/webapp/app/course/competencies/components/course-competency-explanation-modal/course-competency-explanation-modal.component.ts @@ -2,13 +2,11 @@ import { Component, inject } from '@angular/core'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { faXmark } from '@fortawesome/free-solid-svg-icons'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; -import { CompetencyGraphComponent } from 'app/course/learning-paths/components/competency-graph/competency-graph.component'; import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-course-competency-explanation-modal', - standalone: true, - imports: [CompetencyGraphComponent, TranslateDirective, FontAwesomeModule], + imports: [TranslateDirective, FontAwesomeModule], templateUrl: './course-competency-explanation-modal.component.html', styleUrl: './course-competency-explanation-modal.component.scss', }) diff --git a/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.ts b/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.ts index 7f173f4968a3..9a3fb9df3760 100644 --- a/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.ts +++ b/src/main/webapp/app/course/competencies/components/course-competency-relation-form/course-competency-relation-form.component.ts @@ -7,7 +7,6 @@ import { faSpinner } from '@fortawesome/free-solid-svg-icons'; @Component({ selector: 'jhi-course-competency-relation-form', - standalone: true, imports: [ArtemisSharedCommonModule], templateUrl: './course-competency-relation-form.component.html', styleUrl: './course-competency-relation-form.component.scss', @@ -44,7 +43,7 @@ export class CourseCompetencyRelationFormComponent { readonly showCircularDependencyError = computed(() => this.tailCompetencyId() && !this.selectableTailCourseCompetencyIds().includes(this.tailCompetencyId()!)); constructor() { - effect(() => this.selectRelation(this.selectedRelationId()), { allowSignalWrites: true }); + effect(() => this.selectRelation(this.selectedRelationId())); } protected isCourseCompetencySelectable(courseCompetencyId: number): boolean { diff --git a/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.ts b/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.ts index 48708b6ed23c..97dd903908f7 100644 --- a/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.ts +++ b/src/main/webapp/app/course/competencies/components/course-competency-relation-node/course-competency-relation-node.component.ts @@ -9,7 +9,6 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; @Component({ selector: 'jhi-course-competency-relation-node', - standalone: true, imports: [NgClass, TranslateDirective, NgbTooltipModule, ArtemisSharedModule], templateUrl: './course-competency-relation-node.component.html', styleUrl: './course-competency-relation-node.component.scss', diff --git a/src/main/webapp/app/course/competencies/components/import-all-course-competencies-modal/import-all-course-competencies-modal.component.ts b/src/main/webapp/app/course/competencies/components/import-all-course-competencies-modal/import-all-course-competencies-modal.component.ts index 5ef319c926d2..b4f5b880d434 100644 --- a/src/main/webapp/app/course/competencies/components/import-all-course-competencies-modal/import-all-course-competencies-modal.component.ts +++ b/src/main/webapp/app/course/competencies/components/import-all-course-competencies-modal/import-all-course-competencies-modal.component.ts @@ -12,6 +12,7 @@ import { ImportCourseCompetenciesSettingsComponent, } from 'app/course/competencies/components/import-course-competencies-settings/import-course-competencies-settings.component'; import { CourseCompetencyImportOptionsDTO } from 'app/entities/competency.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; const tableColumns: Column[] = [ { @@ -35,8 +36,7 @@ export interface ImportAllCourseCompetenciesResult { @Component({ selector: 'jhi-import-all-course-competencies-modal', - standalone: true, - imports: [ArtemisSharedCommonModule, ImportTableComponent, ImportCourseCompetenciesSettingsComponent], + imports: [ArtemisSharedCommonModule, ImportTableComponent, ImportCourseCompetenciesSettingsComponent, FaIconComponent], providers: [ { provide: PagingService, @@ -55,7 +55,7 @@ export class ImportAllCourseCompetenciesModalComponent { readonly courseId = input.required(); readonly disabledIds = computed(() => [+this.courseId()]); - readonly importSettings = signal(new CourseCompetencyImportSettings()); + importSettings = signal(new CourseCompetencyImportSettings()); public selectCourse(course: Course): void { const courseCompetencyImportOptions = { diff --git a/src/main/webapp/app/course/competencies/components/import-course-competencies-settings/import-course-competencies-settings.component.ts b/src/main/webapp/app/course/competencies/components/import-course-competencies-settings/import-course-competencies-settings.component.ts index 18ad7e777499..641b41b90334 100644 --- a/src/main/webapp/app/course/competencies/components/import-course-competencies-settings/import-course-competencies-settings.component.ts +++ b/src/main/webapp/app/course/competencies/components/import-course-competencies-settings/import-course-competencies-settings.component.ts @@ -1,10 +1,10 @@ import { CommonModule } from '@angular/common'; import { Component, computed, model } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { OwlDateTimeModule, OwlNativeDateTimeModule } from '@danielmoncada/angular-datetime-picker'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { faCalendarAlt, faCircleXmark, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { FormDateTimePickerModule } from 'app/shared/date-time-picker/date-time-picker.module'; -import { FormsModule } from 'app/forms/forms.module'; import { ArtemisSharedModule } from 'app/shared/shared.module'; export class CourseCompetencyImportSettings { @@ -17,7 +17,6 @@ export class CourseCompetencyImportSettings { @Component({ selector: 'jhi-import-course-competencies-settings', - standalone: true, imports: [FormDateTimePickerModule, FormsModule, CommonModule, FontAwesomeModule, OwlDateTimeModule, OwlNativeDateTimeModule, ArtemisSharedModule], templateUrl: './import-course-competencies-settings.component.html', styleUrl: './import-course-competencies-settings.component.scss', diff --git a/src/main/webapp/app/course/competencies/course-competency.service.ts b/src/main/webapp/app/course/competencies/course-competency.service.ts index 6e2f4c4046a8..aec314d7516c 100644 --- a/src/main/webapp/app/course/competencies/course-competency.service.ts +++ b/src/main/webapp/app/course/competencies/course-competency.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { @@ -40,14 +40,12 @@ type CompetencyJolMapResponseType = HttpResponse<{ providedIn: 'root', }) export class CourseCompetencyService { - protected resourceURL = 'api'; + protected httpClient = inject(HttpClient); + protected entityTitleService = inject(EntityTitleService); + protected lectureUnitService = inject(LectureUnitService); + protected accountService = inject(AccountService); - constructor( - protected httpClient: HttpClient, - protected entityTitleService: EntityTitleService, - protected lectureUnitService: LectureUnitService, - protected accountService: AccountService, - ) {} + protected resourceURL = 'api'; getForImport(pageable: CompetencyPageableSearch) { const params = this.createCompetencySearchHttpParams(pageable); diff --git a/src/main/webapp/app/course/competencies/create/create-competency.component.ts b/src/main/webapp/app/course/competencies/create/create-competency.component.ts index 267d9016631c..60e33916c93f 100644 --- a/src/main/webapp/app/course/competencies/create/create-competency.component.ts +++ b/src/main/webapp/app/course/competencies/create/create-competency.component.ts @@ -1,12 +1,9 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { onError } from 'app/shared/util/global.utils'; import { Competency } from 'app/entities/competency.model'; -import { ActivatedRoute, Router } from '@angular/router'; -import { AlertService } from 'app/core/util/alert.service'; import { CompetencyService } from 'app/course/competencies/competency.service'; import { finalize } from 'rxjs/operators'; import { HttpErrorResponse } from '@angular/common/http'; -import { LectureService } from 'app/lecture/lecture.service'; import { CompetencyFormComponent } from 'app/course/competencies/forms/competency/competency-form.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; @@ -16,22 +13,12 @@ import { CourseCompetencyFormData } from 'app/course/competencies/forms/course-c @Component({ selector: 'jhi-create-competency', templateUrl: './create-competency.component.html', - styles: [], - standalone: true, imports: [ArtemisSharedModule, CompetencyFormComponent, ArtemisSharedComponentModule], }) export class CreateCompetencyComponent extends CreateCourseCompetencyComponent { - competencyToCreate: Competency = new Competency(); + private competencyService = inject(CompetencyService); - constructor( - activatedRoute: ActivatedRoute, - router: Router, - alertService: AlertService, - lectureService: LectureService, - private competencyService: CompetencyService, - ) { - super(activatedRoute, router, alertService, lectureService); - } + competencyToCreate: Competency = new Competency(); createCompetency(formData: CourseCompetencyFormData) { if (!formData?.title) { diff --git a/src/main/webapp/app/course/competencies/create/create-course-competency.component.ts b/src/main/webapp/app/course/competencies/create/create-course-competency.component.ts index 0f7884c980df..f79e6cb7288e 100644 --- a/src/main/webapp/app/course/competencies/create/create-course-competency.component.ts +++ b/src/main/webapp/app/course/competencies/create/create-course-competency.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { onError } from 'app/shared/util/global.utils'; import { ActivatedRoute, Router } from '@angular/router'; import { AlertService } from 'app/core/util/alert.service'; @@ -8,21 +8,21 @@ import { LectureService } from 'app/lecture/lecture.service'; import { Lecture } from 'app/entities/lecture.model'; import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component'; -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class CreateCourseCompetencyComponent implements OnInit { + protected activatedRoute = inject(ActivatedRoute); + protected router = inject(Router); + protected alertService = inject(AlertService); + protected lectureService = inject(LectureService); + readonly documentationType: DocumentationType = 'Competencies'; isLoading: boolean; courseId: number; lecturesWithLectureUnits: Lecture[] = []; - constructor( - protected activatedRoute: ActivatedRoute, - protected router: Router, - protected alertService: AlertService, - protected lectureService: LectureService, - ) {} - ngOnInit(): void { this.isLoading = true; this.activatedRoute diff --git a/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts b/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts index befbdfb3478e..f369a6670ed2 100644 --- a/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts +++ b/src/main/webapp/app/course/competencies/create/create-prerequisite.component.ts @@ -1,14 +1,11 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { onError } from 'app/shared/util/global.utils'; -import { ActivatedRoute, Router } from '@angular/router'; -import { AlertService } from 'app/core/util/alert.service'; import { finalize } from 'rxjs/operators'; import { HttpErrorResponse } from '@angular/common/http'; import { Prerequisite } from 'app/entities/prerequisite.model'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { PrerequisiteFormComponent } from 'app/course/competencies/forms/prerequisite/prerequisite-form.component'; import { CreateCourseCompetencyComponent } from 'app/course/competencies/create/create-course-competency.component'; -import { LectureService } from 'app/lecture/lecture.service'; import { PrerequisiteService } from 'app/course/competencies/prerequisite.service'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { CourseCompetencyFormData } from 'app/course/competencies/forms/course-competency-form.component'; @@ -16,21 +13,12 @@ import { CourseCompetencyFormData } from 'app/course/competencies/forms/course-c @Component({ selector: 'jhi-create-prerequisite', templateUrl: './create-prerequisite.component.html', - standalone: true, imports: [ArtemisSharedModule, PrerequisiteFormComponent, ArtemisSharedComponentModule], }) export class CreatePrerequisiteComponent extends CreateCourseCompetencyComponent { - prerequisiteToCreate: Prerequisite = new Prerequisite(); + private prerequisiteService = inject(PrerequisiteService); - constructor( - activatedRoute: ActivatedRoute, - router: Router, - alertService: AlertService, - lectureService: LectureService, - private prerequisiteService: PrerequisiteService, - ) { - super(activatedRoute, router, alertService, lectureService); - } + prerequisiteToCreate: Prerequisite = new Prerequisite(); createPrerequisite(formData: CourseCompetencyFormData) { if (!formData?.title) { diff --git a/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts b/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts index 7ea6f4b22b76..0f3648211cf2 100644 --- a/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts +++ b/src/main/webapp/app/course/competencies/edit/edit-competency.component.ts @@ -1,12 +1,9 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { onError } from 'app/shared/util/global.utils'; import { Competency } from 'app/entities/competency.model'; -import { ActivatedRoute, Router } from '@angular/router'; -import { AlertService } from 'app/core/util/alert.service'; import { finalize, switchMap, take } from 'rxjs/operators'; import { CompetencyService } from 'app/course/competencies/competency.service'; import { HttpErrorResponse } from '@angular/common/http'; -import { LectureService } from 'app/lecture/lecture.service'; import { combineLatest, forkJoin } from 'rxjs'; import { CompetencyFormComponent } from 'app/course/competencies/forms/competency/competency-form.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; @@ -16,23 +13,14 @@ import { CourseCompetencyFormData } from 'app/course/competencies/forms/course-c @Component({ selector: 'jhi-edit-competency', templateUrl: './edit-competency.component.html', - standalone: true, imports: [ArtemisSharedModule, CompetencyFormComponent], }) export class EditCompetencyComponent extends EditCourseCompetencyComponent implements OnInit { + private competencyService = inject(CompetencyService); + competency: Competency; formData: CourseCompetencyFormData; - constructor( - activatedRoute: ActivatedRoute, - lectureService: LectureService, - router: Router, - alertService: AlertService, - private competencyService: CompetencyService, - ) { - super(activatedRoute, lectureService, router, alertService); - } - ngOnInit(): void { super.ngOnInit(); diff --git a/src/main/webapp/app/course/competencies/edit/edit-course-competency.component.ts b/src/main/webapp/app/course/competencies/edit/edit-course-competency.component.ts index d44a05d3cf25..1d370bd85791 100644 --- a/src/main/webapp/app/course/competencies/edit/edit-course-competency.component.ts +++ b/src/main/webapp/app/course/competencies/edit/edit-course-competency.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { onError } from 'app/shared/util/global.utils'; import { ActivatedRoute, Router } from '@angular/router'; import { AlertService } from 'app/core/util/alert.service'; @@ -8,19 +8,19 @@ import { LectureService } from 'app/lecture/lecture.service'; import { Lecture } from 'app/entities/lecture.model'; import { LectureUnitType } from 'app/entities/lecture-unit/lectureUnit.model'; -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class EditCourseCompetencyComponent implements OnInit { + protected activatedRoute = inject(ActivatedRoute); + protected lectureService = inject(LectureService); + protected router = inject(Router); + protected alertService = inject(AlertService); + isLoading = false; lecturesWithLectureUnits: Lecture[] = []; courseId: number; - constructor( - protected activatedRoute: ActivatedRoute, - protected lectureService: LectureService, - protected router: Router, - protected alertService: AlertService, - ) {} - ngOnInit(): void { this.isLoading = true; this.activatedRoute.parent?.parent?.paramMap diff --git a/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts b/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts index d11ecd71c7fb..c8cf992c24b5 100644 --- a/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts +++ b/src/main/webapp/app/course/competencies/edit/edit-prerequisite.component.ts @@ -1,10 +1,7 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { onError } from 'app/shared/util/global.utils'; -import { ActivatedRoute, Router } from '@angular/router'; -import { AlertService } from 'app/core/util/alert.service'; import { finalize, switchMap, take } from 'rxjs/operators'; import { HttpErrorResponse } from '@angular/common/http'; -import { LectureService } from 'app/lecture/lecture.service'; import { combineLatest, forkJoin } from 'rxjs'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { EditCourseCompetencyComponent } from 'app/course/competencies/edit/edit-course-competency.component'; @@ -16,23 +13,14 @@ import { CourseCompetencyFormData } from 'app/course/competencies/forms/course-c @Component({ selector: 'jhi-edit-prerequisite', templateUrl: './edit-prerequisite.component.html', - standalone: true, imports: [ArtemisSharedModule, PrerequisiteFormComponent], }) export class EditPrerequisiteComponent extends EditCourseCompetencyComponent implements OnInit { + private prerequisiteService = inject(PrerequisiteService); + prerequisite: Prerequisite; formData: CourseCompetencyFormData; - constructor( - activatedRoute: ActivatedRoute, - lectureService: LectureService, - router: Router, - alertService: AlertService, - private prerequisiteService: PrerequisiteService, - ) { - super(activatedRoute, lectureService, router, alertService); - } - ngOnInit(): void { super.ngOnInit(); diff --git a/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.ts b/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.ts index e7c2cadf3ad4..9b52ec609d18 100644 --- a/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.ts +++ b/src/main/webapp/app/course/competencies/forms/common-course-competency-form.component.ts @@ -1,8 +1,7 @@ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; -import { FormControl, FormGroup } from '@angular/forms'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, inject } from '@angular/core'; +import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { Lecture } from 'app/entities/lecture.model'; import { TranslateService } from '@ngx-translate/core'; -import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; import { CompetencyTaxonomy, CourseCompetency, CourseCompetencyValidators, DEFAULT_MASTERY_THRESHOLD } from 'app/entities/competency.model'; import { faQuestionCircle, faTimes } from '@fortawesome/free-solid-svg-icons'; import { CourseCompetencyFormData } from 'app/course/competencies/forms/course-competency-form.component'; @@ -10,49 +9,42 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { FormDateTimePickerModule } from 'app/shared/date-time-picker/date-time-picker.module'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; import { NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap'; -import { FormsModule } from 'app/forms/forms.module'; import { ArtemisCompetenciesModule } from 'app/course/competencies/competency.module'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { merge } from 'rxjs'; import { ArtemisMarkdownEditorModule } from 'app/shared/markdown-editor/markdown-editor.module'; import { DateTimePickerType } from 'app/shared/date-time-picker/date-time-picker.component'; +import { ArtemisFormsModule } from 'app/forms/artemis-forms.module'; @Component({ selector: 'jhi-common-course-competency-form', templateUrl: './common-course-competency-form.component.html', styleUrls: ['./common-course-competency-form.component.scss'], - standalone: true, imports: [ ArtemisSharedModule, FormDateTimePickerModule, ArtemisMarkdownModule, NgbDropdownModule, - FormsModule, + ArtemisFormsModule, + ReactiveFormsModule, ArtemisCompetenciesModule, ArtemisSharedComponentModule, ArtemisMarkdownEditorModule, ], }) export class CommonCourseCompetencyFormComponent implements OnInit, OnChanges { - @Input() - formData: CourseCompetencyFormData; - @Input() - isEditMode = false; - @Input() - isInConnectMode = false; - @Input() - isInSingleLectureMode = false; - @Input() - lecturesOfCourseWithLectureUnits: Lecture[] = []; - @Input() - averageStudentScore?: number; - @Input() - form: FormGroup; - @Input() - courseCompetency: CourseCompetency; + private translateService = inject(TranslateService); - @Output() - onTitleOrDescriptionChange = new EventEmitter(); + @Input() formData: CourseCompetencyFormData; + @Input() isEditMode = false; + @Input() isInConnectMode = false; + @Input() isInSingleLectureMode = false; + @Input() lecturesOfCourseWithLectureUnits: Lecture[] = []; + @Input() averageStudentScore?: number; + @Input() form: FormGroup; + @Input() courseCompetency: CourseCompetency; + + @Output() onTitleOrDescriptionChange = new EventEmitter(); protected readonly competencyValidators = CourseCompetencyValidators; protected readonly DateTimePickerType = DateTimePickerType; @@ -66,11 +58,6 @@ export class CommonCourseCompetencyFormComponent implements OnInit, OnChanges { protected readonly DEFAULT_MASTERY_THRESHOLD = DEFAULT_MASTERY_THRESHOLD; protected readonly competencyTaxonomy = CompetencyTaxonomy; - constructor( - private translateService: TranslateService, - public lectureUnitService: LectureUnitService, - ) {} - get titleControl() { return this.form.get('title'); } @@ -79,14 +66,6 @@ export class CommonCourseCompetencyFormComponent implements OnInit, OnChanges { return this.form.get('description'); } - get softDueDateControl() { - return this.form.get('softDueDate'); - } - - get optionalControl() { - return this.form.get('optional'); - } - get masteryThresholdControl() { return this.form.get('masteryThreshold'); } @@ -104,11 +83,11 @@ export class CommonCourseCompetencyFormComponent implements OnInit, OnChanges { this.descriptionControl?.markAsDirty(); } - ngOnInit(): void { + ngOnInit() { merge(this.titleControl!.valueChanges, this.descriptionControl!.valueChanges).subscribe(() => this.suggestTaxonomies()); } - ngOnChanges(): void { + ngOnChanges() { if (this.isEditMode && this.formData) { this.setFormValues(this.formData); } diff --git a/src/main/webapp/app/course/competencies/forms/competency/competency-form.component.ts b/src/main/webapp/app/course/competencies/forms/competency/competency-form.component.ts index 7468236ef661..cd92a95f0c7e 100644 --- a/src/main/webapp/app/course/competencies/forms/competency/competency-form.component.ts +++ b/src/main/webapp/app/course/competencies/forms/competency/competency-form.component.ts @@ -1,23 +1,18 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { CompetencyService } from 'app/course/competencies/competency.service'; -import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; import { CourseCompetencyFormComponent, CourseCompetencyFormData } from 'app/course/competencies/forms/course-competency-form.component'; -import { TranslateService } from '@ngx-translate/core'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { CommonCourseCompetencyFormComponent } from 'app/course/competencies/forms/common-course-competency-form.component'; import { Competency } from 'app/entities/competency.model'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @Component({ selector: 'jhi-competency-form', templateUrl: './competency-form.component.html', styleUrls: ['./competency-form.component.scss'], - standalone: true, - imports: [ArtemisSharedModule, CommonCourseCompetencyFormComponent], + imports: [ArtemisSharedModule, CommonCourseCompetencyFormComponent, FormsModule, ReactiveFormsModule], }) export class CompetencyFormComponent extends CourseCompetencyFormComponent implements OnInit, OnChanges { - @Input() - formData: CourseCompetencyFormData = { + @Input() formData: CourseCompetencyFormData = { id: undefined, title: undefined, description: undefined, @@ -26,24 +21,18 @@ export class CompetencyFormComponent extends CourseCompetencyFormComponent imple masteryThreshold: undefined, optional: false, }; - @Input() - competency: Competency; + @Input() competency: Competency; - @Output() - formSubmitted: EventEmitter = new EventEmitter(); + @Output() formSubmitted: EventEmitter = new EventEmitter(); - constructor(fb: FormBuilder, lectureUnitService: LectureUnitService, competencyService: CompetencyService, translateService: TranslateService) { - super(fb, lectureUnitService, competencyService, translateService); - } - - ngOnChanges(): void { + ngOnChanges() { this.initializeForm(); if (this.isEditMode && this.formData) { this.setFormValues(this.formData); } } - ngOnInit(): void { + ngOnInit() { this.initializeForm(); } diff --git a/src/main/webapp/app/course/competencies/forms/course-competency-form.component.ts b/src/main/webapp/app/course/competencies/forms/course-competency-form.component.ts index ab3a23b96d90..c05e338df3d4 100644 --- a/src/main/webapp/app/course/competencies/forms/course-competency-form.component.ts +++ b/src/main/webapp/app/course/competencies/forms/course-competency-form.component.ts @@ -1,14 +1,12 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { of } from 'rxjs'; import { catchError, delay, map, switchMap } from 'rxjs/operators'; import { Lecture } from 'app/entities/lecture.model'; -import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; import { CompetencyTaxonomy, DEFAULT_MASTERY_THRESHOLD } from 'app/entities/competency.model'; import { faQuestionCircle, faTimes } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { CourseCompetencyService } from 'app/course/competencies/course-competency.service'; -import { TranslateService } from '@ngx-translate/core'; /** * Async Validator to make sure that a competency title is unique within a course @@ -49,29 +47,25 @@ export interface CourseCompetencyFormData { masteryThreshold?: number; } -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class CourseCompetencyFormComponent { abstract formData: CourseCompetencyFormData; - @Input() - isEditMode = false; - @Input() - isInConnectMode = false; - @Input() - isInSingleLectureMode = false; - @Input() - courseId: number; - @Input() - lecturesOfCourseWithLectureUnits: Lecture[] = []; - @Input() - averageStudentScore?: number; - @Input() - hasCancelButton: boolean; - - @Output() - onCancel: EventEmitter = new EventEmitter(); - @Output() - formSubmitted: EventEmitter = new EventEmitter(); + private fb = inject(FormBuilder); + private courseCompetencyService = inject(CourseCompetencyService); + + @Input() isEditMode = false; + @Input() isInConnectMode = false; + @Input() isInSingleLectureMode = false; + @Input() courseId: number; + @Input() lecturesOfCourseWithLectureUnits: Lecture[] = []; + @Input() averageStudentScore?: number; + @Input() hasCancelButton: boolean; + + @Output() onCancel: EventEmitter = new EventEmitter(); + @Output() formSubmitted: EventEmitter = new EventEmitter(); form: FormGroup; @@ -79,13 +73,6 @@ export abstract class CourseCompetencyFormComponent { protected readonly faTimes = faTimes; protected readonly faQuestionCircle = faQuestionCircle; - protected constructor( - protected fb: FormBuilder, - protected lectureUnitService: LectureUnitService, - protected courseCompetencyService: CourseCompetencyService, - protected translateService: TranslateService, - ) {} - get titleControl() { return this.form.get('title'); } diff --git a/src/main/webapp/app/course/competencies/forms/prerequisite/prerequisite-form.component.ts b/src/main/webapp/app/course/competencies/forms/prerequisite/prerequisite-form.component.ts index 9ce65f93af81..afafc4d406ce 100644 --- a/src/main/webapp/app/course/competencies/forms/prerequisite/prerequisite-form.component.ts +++ b/src/main/webapp/app/course/competencies/forms/prerequisite/prerequisite-form.component.ts @@ -1,24 +1,19 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; -import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; import { CourseCompetencyFormComponent, CourseCompetencyFormData } from 'app/course/competencies/forms/course-competency-form.component'; -import { TranslateService } from '@ngx-translate/core'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { CommonCourseCompetencyFormComponent } from 'app/course/competencies/forms/common-course-competency-form.component'; import { CourseCompetencyType } from 'app/entities/competency.model'; -import { PrerequisiteService } from 'app/course/competencies/prerequisite.service'; import { Prerequisite } from 'app/entities/prerequisite.model'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; @Component({ selector: 'jhi-prerequisite-form', templateUrl: './prerequisite-form.component.html', styleUrls: ['./prerequisite-form.component.scss'], - standalone: true, - imports: [ArtemisSharedModule, CommonCourseCompetencyFormComponent], + imports: [ArtemisSharedModule, CommonCourseCompetencyFormComponent, FormsModule, ReactiveFormsModule], }) export class PrerequisiteFormComponent extends CourseCompetencyFormComponent implements OnInit, OnChanges { - @Input() - formData: CourseCompetencyFormData = { + @Input() formData: CourseCompetencyFormData = { id: undefined, title: undefined, description: undefined, @@ -27,26 +22,20 @@ export class PrerequisiteFormComponent extends CourseCompetencyFormComponent imp masteryThreshold: undefined, optional: false, }; - @Input() - prerequisite: Prerequisite; + @Input() prerequisite: Prerequisite; - @Output() - formSubmitted: EventEmitter = new EventEmitter(); + @Output() formSubmitted: EventEmitter = new EventEmitter(); readonly CourseCompetencyType = CourseCompetencyType; - constructor(fb: FormBuilder, lectureUnitService: LectureUnitService, prerequisiteService: PrerequisiteService, translateService: TranslateService) { - super(fb, lectureUnitService, prerequisiteService, translateService); - } - - ngOnChanges(): void { + ngOnChanges() { this.initializeForm(); if (this.isEditMode && this.formData) { this.setFormValues(this.formData); } } - ngOnInit(): void { + ngOnInit() { this.initializeForm(); } diff --git a/src/main/webapp/app/course/competencies/generate-competencies/competency-recommendation-detail.component.ts b/src/main/webapp/app/course/competencies/generate-competencies/competency-recommendation-detail.component.ts index 5e44f690d966..1a7eac6e42fa 100644 --- a/src/main/webapp/app/course/competencies/generate-competencies/competency-recommendation-detail.component.ts +++ b/src/main/webapp/app/course/competencies/generate-competencies/competency-recommendation-detail.component.ts @@ -2,13 +2,31 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { CourseCompetencyValidators } from 'app/entities/competency.model'; import { faChevronRight, faPencilAlt, faSave, faTrash, faWrench } from '@fortawesome/free-solid-svg-icons'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; -import { FormGroup, Validators } from '@angular/forms'; +import { FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { CompetencyFormControlsWithViewed } from 'app/course/competencies/generate-competencies/generate-competencies.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { TaxonomySelectComponent } from '../taxonomy-select/taxonomy-select.component'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-competency-recommendation', templateUrl: './competency-recommendation-detail.component.html', styleUrls: ['competency-recommendation-detail.component.scss'], + imports: [ + FormsModule, + ReactiveFormsModule, + FaIconComponent, + TranslateDirective, + ButtonComponent, + NgbCollapse, + MarkdownEditorMonacoComponent, + TaxonomySelectComponent, + HtmlForMarkdownPipe, + ], }) export class CompetencyRecommendationDetailComponent implements OnInit { @Input({ required: true }) form: FormGroup; diff --git a/src/main/webapp/app/course/competencies/generate-competencies/course-description-form.component.ts b/src/main/webapp/app/course/competencies/generate-competencies/course-description-form.component.ts index a466747c670b..803812f6a915 100644 --- a/src/main/webapp/app/course/competencies/generate-competencies/course-description-form.component.ts +++ b/src/main/webapp/app/course/competencies/generate-competencies/course-description-form.component.ts @@ -1,13 +1,21 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { ButtonType } from 'app/shared/components/button.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { IrisLogoButtonComponent } from 'app/iris/iris-logo-button/iris-logo-button.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-course-description-form', templateUrl: './course-description-form.component.html', + imports: [FormsModule, ReactiveFormsModule, TranslateDirective, FaIconComponent, NgbTooltip, IrisLogoButtonComponent, ArtemisTranslatePipe], }) export class CourseDescriptionFormComponent implements OnInit { + private formBuilder = inject(FormBuilder); + @Input() isLoading = false; @Input() placeholder = ''; @Output() formSubmitted: EventEmitter = new EventEmitter(); @@ -23,8 +31,6 @@ export class CourseDescriptionFormComponent implements OnInit { protected readonly DESCRIPTION_MIN = 100; protected readonly ButtonType = ButtonType; - constructor(private formBuilder: FormBuilder) {} - ngOnInit(): void { this.form = this.formBuilder.group({ courseDescription: [this.placeholder, [Validators.required, Validators.minLength(this.DESCRIPTION_MIN), Validators.maxLength(this.DESCRIPTION_MAX)]], diff --git a/src/main/webapp/app/course/competencies/generate-competencies/generate-competencies.component.ts b/src/main/webapp/app/course/competencies/generate-competencies/generate-competencies.component.ts index 19d3d33b6124..65256d6ed5b8 100644 --- a/src/main/webapp/app/course/competencies/generate-competencies/generate-competencies.component.ts +++ b/src/main/webapp/app/course/competencies/generate-competencies/generate-competencies.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; +import { Component, OnInit, ViewChild, inject } from '@angular/core'; import { CompetencyService } from 'app/course/competencies/competency.service'; import { AlertService } from 'app/core/util/alert.service'; import { onError } from 'app/shared/util/global.utils'; @@ -6,7 +6,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Competency, CompetencyTaxonomy } from 'app/entities/competency.model'; import { ActivatedRoute, Router } from '@angular/router'; import { faBan, faSave, faTimes } from '@fortawesome/free-solid-svg-icons'; -import { FormArray, FormBuilder, FormControl, FormGroup } from '@angular/forms'; +import { FormArray, FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { ButtonType } from 'app/shared/components/button.component'; import { ComponentCanDeactivate } from 'app/shared/guard/can-deactivate.model'; import { ConfirmAutofocusModalComponent } from 'app/shared/components/confirm-autofocus-modal.component'; @@ -49,15 +49,26 @@ type CompetencyGenerationStatusUpdate = { @Component({ selector: 'jhi-generate-competencies', templateUrl: './generate-competencies.component.html', - standalone: true, - imports: [ArtemisSharedCommonModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule], + imports: [ArtemisSharedCommonModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule, FormsModule, ReactiveFormsModule], }) export class GenerateCompetenciesComponent implements OnInit, ComponentCanDeactivate { + private courseManagementService = inject(CourseManagementService); + private courseCompetencyService = inject(CourseCompetencyService); + private competencyService = inject(CompetencyService); + private alertService = inject(AlertService); + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private formBuilder = inject(FormBuilder); + private modalService = inject(NgbModal); + private artemisTranslatePipe = inject(ArtemisTranslatePipe); + private translateService = inject(TranslateService); + private jhiWebsocketService = inject(JhiWebsocketService); + @ViewChild(CourseDescriptionFormComponent) courseDescriptionForm: CourseDescriptionFormComponent; courseId: number; isLoading = false; - submitted: boolean = false; + submitted = false; form = new FormGroup({ competencies: new FormArray>([]) }); //Icons @@ -69,20 +80,6 @@ export class GenerateCompetenciesComponent implements OnInit, ComponentCanDeacti protected readonly ButtonType = ButtonType; readonly documentationType: DocumentationType = 'GenerateCompetencies'; - constructor( - private courseManagementService: CourseManagementService, - private courseCompetencyService: CourseCompetencyService, - private competencyService: CompetencyService, - private alertService: AlertService, - private activatedRoute: ActivatedRoute, - private router: Router, - private formBuilder: FormBuilder, - private modalService: NgbModal, - private artemisTranslatePipe: ArtemisTranslatePipe, - private translateService: TranslateService, - private jhiWebsocketService: JhiWebsocketService, - ) {} - ngOnInit(): void { this.activatedRoute.params.subscribe((params) => { this.courseId = Number(params['courseId']); diff --git a/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-competencies.component.ts b/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-competencies.component.ts index 7186b318de76..19c516b94442 100644 --- a/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-competencies.component.ts +++ b/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-competencies.component.ts @@ -1,9 +1,4 @@ -import { ActivatedRoute, Router } from '@angular/router'; -import { Component } from '@angular/core'; -import { AlertService } from 'app/core/util/alert.service'; -import { TranslateService } from '@ngx-translate/core'; -import { SortService } from 'app/shared/service/sort.service'; -import { StandardizedCompetencyService } from 'app/shared/standardized-competencies/standardized-competency.service'; +import { Component, inject } from '@angular/core'; import { CompetencyService } from 'app/course/competencies/competency.service'; import { CourseImportStandardizedCourseCompetenciesComponent } from 'app/course/competencies/import-standardized-competencies/course-import-standardized-course-competencies.component'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; @@ -16,7 +11,6 @@ import { KnowledgeAreaTreeComponent } from 'app/shared/standardized-competencies @Component({ selector: 'jhi-course-import-standardized-competencies', templateUrl: './course-import-standardized-competencies.component.html', - standalone: true, imports: [ ArtemisSharedCommonModule, ArtemisSharedComponentModule, @@ -27,17 +21,7 @@ import { KnowledgeAreaTreeComponent } from 'app/shared/standardized-competencies ], }) export class CourseImportStandardizedCompetenciesComponent extends CourseImportStandardizedCourseCompetenciesComponent { - constructor( - router: Router, - activatedRoute: ActivatedRoute, - standardizedCompetencyService: StandardizedCompetencyService, - alertService: AlertService, - translateService: TranslateService, - sortService: SortService, - private competencyService: CompetencyService, - ) { - super(router, activatedRoute, standardizedCompetencyService, alertService, translateService, sortService); - } + private competencyService = inject(CompetencyService); protected importCompetencies() { super.importCompetencies(this.competencyService); diff --git a/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-course-competencies.component.ts b/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-course-competencies.component.ts index 63b74465efe7..280cdf5f0f80 100644 --- a/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-course-competencies.component.ts +++ b/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-course-competencies.component.ts @@ -10,7 +10,7 @@ import { } from 'app/entities/competency/standardized-competency.model'; import { faBan, faDownLeftAndUpRightToCenter, faFileImport, faSort, faTrash, faUpRightAndDownLeftFromCenter } from '@fortawesome/free-solid-svg-icons'; import { ActivatedRoute, Router } from '@angular/router'; -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { onError } from 'app/shared/util/global.utils'; import { forkJoin, map } from 'rxjs'; import { HttpErrorResponse } from '@angular/common/http'; @@ -34,8 +34,17 @@ interface KnowledgeAreaForImport extends KnowledgeAreaForTree { competencies?: StandardizedCompetencyForImport[]; } -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class CourseImportStandardizedCourseCompetenciesComponent extends StandardizedCompetencyFilterPageComponent implements OnInit, ComponentCanDeactivate { + protected router = inject(Router); + protected activatedRoute = inject(ActivatedRoute); + protected standardizedCompetencyService = inject(StandardizedCompetencyService); + protected alertService = inject(AlertService); + protected translateService = inject(TranslateService); + protected sortService = inject(SortService); + protected selectedCompetencies: StandardizedCompetencyForImport[] = []; protected selectedCompetency?: StandardizedCompetencyForImport; protected sourceString = ''; @@ -57,17 +66,6 @@ export abstract class CourseImportStandardizedCourseCompetenciesComponent extend protected readonly faTrash = faTrash; protected readonly faSort = faSort; - constructor( - protected router: Router, - protected activatedRoute: ActivatedRoute, - protected standardizedCompetencyService: StandardizedCompetencyService, - protected alertService: AlertService, - protected translateService: TranslateService, - protected sortService: SortService, - ) { - super(); - } - ngOnInit(): void { this.isLoading = true; const getKnowledgeAreasObservable = this.standardizedCompetencyService.getAllForTreeView(); diff --git a/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-prerequisites.component.ts b/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-prerequisites.component.ts index ed3e19ff65cc..cad9f80d38f3 100644 --- a/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-prerequisites.component.ts +++ b/src/main/webapp/app/course/competencies/import-standardized-competencies/course-import-standardized-prerequisites.component.ts @@ -1,9 +1,4 @@ -import { ActivatedRoute, Router } from '@angular/router'; -import { Component } from '@angular/core'; -import { AlertService } from 'app/core/util/alert.service'; -import { TranslateService } from '@ngx-translate/core'; -import { SortService } from 'app/shared/service/sort.service'; -import { StandardizedCompetencyService } from 'app/shared/standardized-competencies/standardized-competency.service'; +import { Component, inject } from '@angular/core'; import { PrerequisiteService } from 'app/course/competencies/prerequisite.service'; import { CourseImportStandardizedCourseCompetenciesComponent } from 'app/course/competencies/import-standardized-competencies/course-import-standardized-course-competencies.component'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; @@ -12,11 +7,13 @@ import { StandardizedCompetencyDetailComponent } from 'app/shared/standardized-c import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; import { KnowledgeAreaTreeComponent } from 'app/shared/standardized-competencies/knowledge-area-tree.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; +import { FormsModule } from '@angular/forms'; @Component({ selector: 'jhi-course-import-standardized-prerequisites', templateUrl: './course-import-standardized-prerequisites.component.html', - standalone: true, imports: [ ArtemisSharedCommonModule, ArtemisSharedComponentModule, @@ -24,20 +21,13 @@ import { KnowledgeAreaTreeComponent } from 'app/shared/standardized-competencies StandardizedCompetencyFilterComponent, StandardizedCompetencyDetailComponent, KnowledgeAreaTreeComponent, + FaIconComponent, + FormsModule, + NgbTooltipModule, ], }) export class CourseImportStandardizedPrerequisitesComponent extends CourseImportStandardizedCourseCompetenciesComponent { - constructor( - router: Router, - activatedRoute: ActivatedRoute, - standardizedCompetencyService: StandardizedCompetencyService, - alertService: AlertService, - translateService: TranslateService, - sortService: SortService, - private prerequisiteService: PrerequisiteService, - ) { - super(router, activatedRoute, standardizedCompetencyService, alertService, translateService, sortService); - } + private prerequisiteService = inject(PrerequisiteService); protected importCompetencies() { super.importCompetencies(this.prerequisiteService); diff --git a/src/main/webapp/app/course/competencies/import/competency-search.component.ts b/src/main/webapp/app/course/competencies/import/competency-search.component.ts index 24b924a45c37..b740becc121e 100644 --- a/src/main/webapp/app/course/competencies/import/competency-search.component.ts +++ b/src/main/webapp/app/course/competencies/import/competency-search.component.ts @@ -3,10 +3,16 @@ import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'; import { ButtonType } from 'app/shared/components/button.component'; import { getSemesters } from 'app/utils/semester-utils'; import { CourseCompetencyFilter } from 'app/shared/table/pageable-table'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-competency-search', templateUrl: './competency-search.component.html', + imports: [TranslateDirective, FormsModule, NgbCollapse, ButtonComponent, FaIconComponent], }) export class CompetencySearchComponent { @Input() search: CourseCompetencyFilter; diff --git a/src/main/webapp/app/course/competencies/import/import-competencies-table.component.ts b/src/main/webapp/app/course/competencies/import/import-competencies-table.component.ts index 9b97b7547193..87146057a246 100644 --- a/src/main/webapp/app/course/competencies/import/import-competencies-table.component.ts +++ b/src/main/webapp/app/course/competencies/import/import-competencies-table.component.ts @@ -2,10 +2,18 @@ import { Component, ContentChild, EventEmitter, Input, OnInit, Output, TemplateR import { faSort } from '@fortawesome/free-solid-svg-icons'; import { PageableSearch, SearchResult, SortingOrder } from 'app/shared/table/pageable-table'; import { Competency } from 'app/entities/competency.model'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgTemplateOutlet } from '@angular/common'; +import { NgbPagination } from '@ng-bootstrap/ng-bootstrap'; +import { HtmlForMarkdownPipe } from 'app/shared/pipes/html-for-markdown.pipe'; @Component({ selector: 'jhi-import-competencies-table', templateUrl: './import-competencies-table.component.html', + imports: [SortDirective, SortByDirective, FaIconComponent, TranslateDirective, NgTemplateOutlet, NgbPagination, HtmlForMarkdownPipe], }) export class ImportCompetenciesTableComponent implements OnInit { @Input() content: SearchResult; diff --git a/src/main/webapp/app/course/competencies/import/import-competencies.component.ts b/src/main/webapp/app/course/competencies/import/import-competencies.component.ts index a9adf3785ee5..9dd6cf1181b9 100644 --- a/src/main/webapp/app/course/competencies/import/import-competencies.component.ts +++ b/src/main/webapp/app/course/competencies/import/import-competencies.component.ts @@ -11,7 +11,6 @@ import { CourseCompetencyType } from 'app/entities/competency.model'; @Component({ selector: 'jhi-import-competencies', templateUrl: './import-course-competencies.component.html', - standalone: true, imports: [ArtemisSharedCommonModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule], }) export class ImportCompetenciesComponent extends ImportCourseCompetenciesComponent { diff --git a/src/main/webapp/app/course/competencies/import/import-course-competencies.component.ts b/src/main/webapp/app/course/competencies/import/import-course-competencies.component.ts index 0190d71b8b5e..c368991bedc4 100644 --- a/src/main/webapp/app/course/competencies/import/import-course-competencies.component.ts +++ b/src/main/webapp/app/course/competencies/import/import-course-competencies.component.ts @@ -16,12 +16,14 @@ import { CourseCompetencyService } from 'app/course/competencies/course-competen * An abstract component used to import course competencies. Its concrete implementations are * {@link ImportCompetenciesComponent} and {@link ImportPrerequisitesComponent} */ -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class ImportCourseCompetenciesComponent implements OnInit, ComponentCanDeactivate { // this attribute has to be set when using the common template (import-course-competencies.component.html) abstract entityType: string; // set this attribute to hide the options to import relation - allowRelationImport: boolean = false; + allowRelationImport = false; courseId: number; isLoading = false; diff --git a/src/main/webapp/app/course/competencies/import/import-prerequisites.component.ts b/src/main/webapp/app/course/competencies/import/import-prerequisites.component.ts index 660c12d399b2..554807741494 100644 --- a/src/main/webapp/app/course/competencies/import/import-prerequisites.component.ts +++ b/src/main/webapp/app/course/competencies/import/import-prerequisites.component.ts @@ -11,7 +11,6 @@ import { CourseCompetencyType } from 'app/entities/competency.model'; @Component({ selector: 'jhi-import-prerequisites', templateUrl: './import-course-competencies.component.html', - standalone: true, imports: [ArtemisSharedCommonModule, ArtemisSharedComponentModule, ArtemisCompetenciesModule], }) export class ImportPrerequisitesComponent extends ImportCourseCompetenciesComponent { diff --git a/src/main/webapp/app/course/competencies/judgement-of-learning-rating/judgement-of-learning-rating.component.ts b/src/main/webapp/app/course/competencies/judgement-of-learning-rating/judgement-of-learning-rating.component.ts index 92fa6f2d63e7..e02ce29d0bb3 100644 --- a/src/main/webapp/app/course/competencies/judgement-of-learning-rating/judgement-of-learning-rating.component.ts +++ b/src/main/webapp/app/course/competencies/judgement-of-learning-rating/judgement-of-learning-rating.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { RatingModule } from 'app/exercises/shared/rating/rating.module'; import { StarRatingComponent } from 'app/exercises/shared/rating/star-rating/star-rating.component'; import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @@ -8,11 +8,13 @@ import { CourseCompetencyService } from 'app/course/competencies/course-competen @Component({ selector: 'jhi-judgement-of-learning-rating', - standalone: true, imports: [RatingModule, ArtemisSharedCommonModule, ArtemisSharedComponentModule], templateUrl: './judgement-of-learning-rating.component.html', }) export class JudgementOfLearningRatingComponent { + private courseCompetencyService = inject(CourseCompetencyService); + private alertService = inject(AlertService); + @Input() courseId: number | undefined; @Input() competencyId: number; @Input() rating: number | undefined; @@ -20,11 +22,6 @@ export class JudgementOfLearningRatingComponent { @Output() ratingChange = new EventEmitter(); - constructor( - private courseCompetencyService: CourseCompetencyService, - private alertService: AlertService, - ) {} - /** * Handle the event when a new rating is selected. * @param event - starRating component that holds the new rating value diff --git a/src/main/webapp/app/course/competencies/prerequisite.service.ts b/src/main/webapp/app/course/competencies/prerequisite.service.ts index 3554ef546f38..ff75cfc5fa73 100644 --- a/src/main/webapp/app/course/competencies/prerequisite.service.ts +++ b/src/main/webapp/app/course/competencies/prerequisite.service.ts @@ -1,11 +1,8 @@ -import { HttpClient, HttpResponse } from '@angular/common/http'; +import { HttpResponse } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { map, tap } from 'rxjs/operators'; import { CompetencyImportResponseDTO, CompetencyWithTailRelationDTO, CourseCompetency, CourseCompetencyImportOptionsDTO } from 'app/entities/competency.model'; -import { EntityTitleService } from 'app/shared/layouts/navbar/entity-title.service'; -import { LectureUnitService } from 'app/lecture/lecture-unit/lecture-unit-management/lectureUnit.service'; -import { AccountService } from 'app/core/auth/account.service'; import { Prerequisite } from 'app/entities/prerequisite.model'; import { CourseCompetencyService } from 'app/course/competencies/course-competency.service'; @@ -16,10 +13,6 @@ type EntityArrayResponseType = HttpResponse; providedIn: 'root', }) export class PrerequisiteService extends CourseCompetencyService { - constructor(httpClient: HttpClient, entityTitleService: EntityTitleService, lectureUnitService: LectureUnitService, accountService: AccountService) { - super(httpClient, entityTitleService, lectureUnitService, accountService); - } - getAllForCourse(courseId: number): Observable { return this.httpClient.get(`${this.resourceURL}/courses/${courseId}/prerequisites`, { observe: 'response' }).pipe( map((res: EntityArrayResponseType) => this.convertArrayResponseDatesFromServer(res)), diff --git a/src/main/webapp/app/course/competencies/taxonomy-select/taxonomy-select.component.ts b/src/main/webapp/app/course/competencies/taxonomy-select/taxonomy-select.component.ts index 653b5993ff6c..2bcb8e5b0467 100644 --- a/src/main/webapp/app/course/competencies/taxonomy-select/taxonomy-select.component.ts +++ b/src/main/webapp/app/course/competencies/taxonomy-select/taxonomy-select.component.ts @@ -1,10 +1,13 @@ import { Component, Input } from '@angular/core'; -import { FormControl } from '@angular/forms'; +import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { CompetencyTaxonomy } from 'app/entities/competency.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { KeyValuePipe } from '@angular/common'; @Component({ selector: 'jhi-taxonomy-select', templateUrl: './taxonomy-select.component.html', + imports: [FormsModule, ReactiveFormsModule, TranslateDirective, KeyValuePipe], }) export class TaxonomySelectComponent { /** diff --git a/src/main/webapp/app/course/course-access-storage.service.ts b/src/main/webapp/app/course/course-access-storage.service.ts index dcbf08e519df..e4a85450c000 100644 --- a/src/main/webapp/app/course/course-access-storage.service.ts +++ b/src/main/webapp/app/course/course-access-storage.service.ts @@ -1,18 +1,18 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { LocalStorageService } from 'ngx-webstorage'; @Injectable({ providedIn: 'root', }) export class CourseAccessStorageService { + private localStorage = inject(LocalStorageService); + public static readonly STORAGE_KEY = 'artemis.courseAccess'; public static readonly STORAGE_KEY_DROPDOWN = 'artemis.courseAccessDropdown'; public static readonly MAX_DISPLAYED_RECENTLY_ACCESSED_COURSES_OVERVIEW = 3; // Maximum number of recently accessed courses displayed in the dropdown, including the current course. The current course will be removed before displaying the dropdown so only 6 - 1 courses will be displayed in the dropdown. public static readonly MAX_DISPLAYED_RECENTLY_ACCESSED_COURSES_DROPDOWN = 6; - constructor(private localStorage: LocalStorageService) {} - onCourseAccessed(courseId: number, storageKey: string, maxAccessedCourses: number): void { const courseAccessMap: { [key: number]: number } = this.localStorage.retrieve(storageKey) || {}; diff --git a/src/main/webapp/app/course/course-for-import-dto-paging-service.ts b/src/main/webapp/app/course/course-for-import-dto-paging-service.ts index 6b311405c5de..c332daaa0128 100644 --- a/src/main/webapp/app/course/course-for-import-dto-paging-service.ts +++ b/src/main/webapp/app/course/course-for-import-dto-paging-service.ts @@ -1,5 +1,5 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { PagingService } from 'app/exercises/shared/manage/paging.service'; import { SearchResult, SearchTermPageableSearch } from 'app/shared/table/pageable-table'; import { Observable } from 'rxjs'; @@ -10,9 +10,11 @@ type EntityResponseType = SearchResult; @Injectable({ providedIn: 'root' }) export class CourseForImportDTOPagingService extends PagingService { + private http = inject(HttpClient); + private readonly RESOURCE_URL = 'api/courses'; - constructor(private http: HttpClient) { + constructor() { super(); } diff --git a/src/main/webapp/app/course/course-scores/course-scores-routing.module.ts b/src/main/webapp/app/course/course-scores/course-scores-routing.module.ts index eb2b5374af44..5f6227f0a7fb 100644 --- a/src/main/webapp/app/course/course-scores/course-scores-routing.module.ts +++ b/src/main/webapp/app/course/course-scores/course-scores-routing.module.ts @@ -1,19 +1,18 @@ import { RouterModule, Routes } from '@angular/router'; -import { CourseScoresComponent } from 'app/course/course-scores/course-scores.component'; + import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { NgModule } from '@angular/core'; import { Authority } from 'app/shared/constants/authority.constants'; import { CourseManagementResolve } from 'app/course/manage/course-management-resolve.service'; -import { CourseManagementTabBarComponent } from 'app/course/manage/course-management-tab-bar/course-management-tab-bar.component'; const routes: Routes = [ { path: ':courseId/scores', - component: CourseManagementTabBarComponent, + loadComponent: () => import('app/course/manage/course-management-tab-bar/course-management-tab-bar.component').then((m) => m.CourseManagementTabBarComponent), children: [ { path: '', - component: CourseScoresComponent, + loadComponent: () => import('app/course/course-scores/course-scores.component').then((m) => m.CourseScoresComponent), resolve: { course: CourseManagementResolve, }, diff --git a/src/main/webapp/app/course/course-scores/course-scores.component.ts b/src/main/webapp/app/course/course-scores/course-scores.component.ts index 530e70f9ab0e..29ebd7eebb16 100644 --- a/src/main/webapp/app/course/course-scores/course-scores.component.ts +++ b/src/main/webapp/app/course/course-scores/course-scores.component.ts @@ -1,6 +1,6 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { Subscription, forkJoin, of } from 'rxjs'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import dayjs from 'dayjs/esm'; import { sum } from 'lodash-es'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; @@ -48,6 +48,15 @@ import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagi import { GradeStep } from 'app/entities/grade-step.model'; import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase'; import { PlagiarismVerdict } from 'app/exercises/shared/plagiarism/types/PlagiarismVerdict'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ParticipantScoresDistributionComponent } from 'app/shared/participant-scores/participant-scores-distribution/participant-scores-distribution.component'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { NgClass } from '@angular/common'; +import { ExportButtonComponent } from 'app/shared/export/export-button.component'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export enum HighlightType { AVERAGE = 'average', @@ -60,8 +69,31 @@ export enum HighlightType { templateUrl: './course-scores.component.html', styleUrls: ['./course-scores.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + TranslateDirective, + RouterLink, + FaIconComponent, + ParticipantScoresDistributionComponent, + NgbTooltip, + NgClass, + ExportButtonComponent, + SortDirective, + SortByDirective, + ArtemisTranslatePipe, + ], }) export class CourseScoresComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private courseService = inject(CourseManagementService); + private sortService = inject(SortService); + private changeDetector = inject(ChangeDetectorRef); + private languageHelper = inject(JhiLanguageHelper); + private localeConversionService = inject(LocaleConversionService); + private participantScoresService = inject(ParticipantScoresService); + private gradingSystemService = inject(GradingSystemService); + private navigationUtilService = inject(ArtemisNavigationUtilService); + private plagiarismCasesService = inject(PlagiarismCasesService); + private paramSub: Subscription; private languageChangeSubscription?: Subscription; @@ -132,18 +164,7 @@ export class CourseScoresComponent implements OnInit, OnDestroy { faSpinner = faSpinner; faClipboard = faClipboard; - constructor( - private route: ActivatedRoute, - private courseService: CourseManagementService, - private sortService: SortService, - private changeDetector: ChangeDetectorRef, - private languageHelper: JhiLanguageHelper, - private localeConversionService: LocaleConversionService, - private participantScoresService: ParticipantScoresService, - private gradingSystemService: GradingSystemService, - private navigationUtilService: ArtemisNavigationUtilService, - private plagiarismCasesService: PlagiarismCasesService, - ) { + constructor() { this.reverse = false; this.predicate = 'id'; } diff --git a/src/main/webapp/app/course/course-scores/course-scores.module.ts b/src/main/webapp/app/course/course-scores/course-scores.module.ts index 23b0e3417817..3936ab34157d 100644 --- a/src/main/webapp/app/course/course-scores/course-scores.module.ts +++ b/src/main/webapp/app/course/course-scores/course-scores.module.ts @@ -6,7 +6,6 @@ import { ArtemisParticipantScoresModule } from 'app/shared/participant-scores/pa import { ExportModule } from 'app/shared/export/export.module'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisCourseScoresRoutingModule, ArtemisParticipantScoresModule, ExportModule], - declarations: [CourseScoresComponent], + imports: [ArtemisSharedModule, ArtemisCourseScoresRoutingModule, ArtemisParticipantScoresModule, ExportModule, CourseScoresComponent], }) export class ArtemisCourseScoresModule {} diff --git a/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard-information.component.ts b/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard-information.component.ts index 36819ac62b0f..773fa015051c 100644 --- a/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard-information.component.ts +++ b/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard-information.component.ts @@ -37,7 +37,6 @@ export class AssessmentDashboardInformationEntry { selector: 'jhi-assessment-dashboard-information', templateUrl: './assessment-dashboard-information.component.html', styleUrls: ['./assessment-dashboard-information.component.scss'], - standalone: true, imports: [TranslateDirective, PieChartModule, ArtemisSidePanelModule, RouterLink, ArtemisTranslatePipe], }) export class AssessmentDashboardInformationComponent implements OnInit, OnChanges, OnDestroy { @@ -97,11 +96,11 @@ export class AssessmentDashboardInformationComponent implements OnInit, OnChange ]; } - ngOnChanges(): void { + ngOnChanges() { this.setup(); } - ngOnDestroy(): void { + ngOnDestroy() { this.themeSubscription?.unsubscribe(); } diff --git a/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts b/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts index 8ada9ad9321b..0678c09f3f34 100644 --- a/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts +++ b/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.component.ts @@ -39,7 +39,6 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; templateUrl: './assessment-dashboard.component.html', styleUrls: ['./exam-assessment-buttons/exam-assessment-buttons.component.scss'], providers: [CourseManagementService], - standalone: true, imports: [ ArtemisSharedComponentModule, RouterLink, diff --git a/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.route.ts b/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.route.ts index 178648a1d3b4..bc5742db0660 100644 --- a/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.route.ts +++ b/src/main/webapp/app/course/dashboards/assessment-dashboard/assessment-dashboard.route.ts @@ -1,18 +1,17 @@ import { Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { AssessmentDashboardComponent } from './assessment-dashboard.component'; + import { Authority } from 'app/shared/constants/authority.constants'; import { CourseManagementResolve } from 'app/course/manage/course-management-resolve.service'; -import { CourseManagementTabBarComponent } from 'app/course/manage/course-management-tab-bar/course-management-tab-bar.component'; export const assessmentDashboardRoute: Routes = [ { path: ':courseId/assessment-dashboard', - component: CourseManagementTabBarComponent, + loadComponent: () => import('app/course/manage/course-management-tab-bar/course-management-tab-bar.component').then((m) => m.CourseManagementTabBarComponent), children: [ { path: '', - component: AssessmentDashboardComponent, + loadComponent: () => import('./assessment-dashboard.component').then((m) => m.AssessmentDashboardComponent), resolve: { course: CourseManagementResolve, }, diff --git a/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.ts b/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.ts index de7010811fae..4014718b261b 100644 --- a/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.ts +++ b/src/main/webapp/app/course/dashboards/assessment-dashboard/exam-assessment-buttons/exam-assessment-buttons.component.ts @@ -21,7 +21,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-exam-assessment-buttons', templateUrl: './exam-assessment-buttons.component.html', - standalone: true, imports: [RouterLink, FaIconComponent, TranslateDirective], }) export class ExamAssessmentButtonsComponent implements OnInit { diff --git a/src/main/webapp/app/course/dashboards/due-date-stat.model.ts b/src/main/webapp/app/course/dashboards/due-date-stat.model.ts index bcf244fc9fcb..727c63fcd25a 100644 --- a/src/main/webapp/app/course/dashboards/due-date-stat.model.ts +++ b/src/main/webapp/app/course/dashboards/due-date-stat.model.ts @@ -6,8 +6,6 @@ export class DueDateStat { public inTime = 0; public late = 0; - constructor() {} - /** * Computed property to get the total number of * both properties, inTime and late members. diff --git a/src/main/webapp/app/course/dashboards/stats-for-dashboard.model.ts b/src/main/webapp/app/course/dashboards/stats-for-dashboard.model.ts index 2af8e2ed1c02..e46452e5a0d4 100644 --- a/src/main/webapp/app/course/dashboards/stats-for-dashboard.model.ts +++ b/src/main/webapp/app/course/dashboards/stats-for-dashboard.model.ts @@ -20,8 +20,6 @@ export class StatsForDashboard { public tutorLeaderboardEntries: TutorLeaderboardElement[] = []; - constructor() {} - /** * Correctly initializes a class instance from a typecasted object. * Returns a 'real' class instance that supports all class methods. diff --git a/src/main/webapp/app/course/learning-paths/components/competency-graph-modal/competency-graph-modal.component.ts b/src/main/webapp/app/course/learning-paths/components/competency-graph-modal/competency-graph-modal.component.ts index 28594a2f49d9..0a076f93aa71 100644 --- a/src/main/webapp/app/course/learning-paths/components/competency-graph-modal/competency-graph-modal.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/competency-graph-modal/competency-graph-modal.component.ts @@ -10,7 +10,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-competency-graph-modal', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [FontAwesomeModule, CompetencyGraphComponent, TranslateDirective], templateUrl: './competency-graph-modal.component.html', @@ -29,13 +28,10 @@ export class CompetencyGraphModalComponent { private readonly activeModal: NgbActiveModal = inject(NgbActiveModal); constructor() { - effect( - () => { - const learningPathId = this.learningPathId(); - untracked(() => this.loadCompetencyGraph(learningPathId)); - }, - { allowSignalWrites: true }, - ); + effect(() => { + const learningPathId = this.learningPathId(); + untracked(() => this.loadCompetencyGraph(learningPathId)); + }); } private async loadCompetencyGraph(learningPathId: number): Promise { diff --git a/src/main/webapp/app/course/learning-paths/components/competency-graph/competency-graph.component.ts b/src/main/webapp/app/course/learning-paths/components/competency-graph/competency-graph.component.ts index bf177640eb57..91030c0983cf 100644 --- a/src/main/webapp/app/course/learning-paths/components/competency-graph/competency-graph.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/competency-graph/competency-graph.component.ts @@ -6,7 +6,6 @@ import { CompetencyNodeComponent, SizeUpdate } from 'app/course/learning-paths/c @Component({ selector: 'jhi-competency-graph', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CompetencyNodeComponent, NgxGraphModule], templateUrl: './competency-graph.component.html', @@ -34,7 +33,7 @@ export class CompetencyGraphComponent { readonly zoomToFit$ = new Subject(); constructor() { - effect(() => this.internalCompetencyGraph.set(this.competencyGraph()), { allowSignalWrites: true }); + effect(() => this.internalCompetencyGraph.set(this.competencyGraph())); } setNodeDimension(sizeUpdate: SizeUpdate): void { diff --git a/src/main/webapp/app/course/learning-paths/components/competency-node/competency-node.component.ts b/src/main/webapp/app/course/learning-paths/components/competency-node/competency-node.component.ts index e4d60b32ef47..c4e951137203 100644 --- a/src/main/webapp/app/course/learning-paths/components/competency-node/competency-node.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/competency-node/competency-node.component.ts @@ -12,7 +12,6 @@ export interface SizeUpdate { @Component({ selector: 'jhi-learning-path-competency-node', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgbDropdownModule, FontAwesomeModule, NgbAccordionModule, CommonModule], templateUrl: './competency-node.component.html', diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-exercise/learning-path-exercise.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-path-exercise/learning-path-exercise.component.ts index 99f230ce1938..028748ebf32f 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-exercise/learning-path-exercise.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-exercise/learning-path-exercise.component.ts @@ -4,7 +4,6 @@ import { CourseExerciseDetailsModule } from 'app/overview/exercise-details/cours @Component({ selector: 'jhi-learning-path-exercise', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CourseExerciseDetailsModule], templateUrl: './learning-path-exercise.component.html', diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.ts index 04822ff754d8..a8303af06344 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-lecture-unit/learning-path-lecture-unit.component.ts @@ -16,7 +16,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-learning-path-lecture-unit', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [ArtemisLectureUnitsModule, VideoUnitComponent, TextUnitComponent, AttachmentUnitComponent, OnlineUnitComponent, DiscussionSectionComponent, TranslateDirective], templateUrl: './learning-path-lecture-unit.component.html', @@ -37,13 +36,10 @@ export class LearningPathLectureUnitComponent { readonly isCommunicationEnabled = computed(() => isCommunicationEnabled(this.lecture()?.course)); constructor() { - effect( - () => { - const lectureUnitId = this.lectureUnitId(); - untracked(() => this.loadLectureUnit(lectureUnitId)); - }, - { allowSignalWrites: true }, - ); + effect(() => { + const lectureUnitId = this.lectureUnitId(); + untracked(() => this.loadLectureUnit(lectureUnitId)); + }); } async loadLectureUnit(lectureUnitId: number): Promise { diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts index 21c0fa4c1c32..3ca8225316d4 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview-learning-objects/learning-path-nav-overview-learning-objects.component.ts @@ -11,7 +11,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-learning-path-nav-overview-learning-objects', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgbAccordionModule, FontAwesomeModule, CommonModule, TranslateDirective], templateUrl: './learning-path-nav-overview-learning-objects.component.html', @@ -41,12 +40,9 @@ export class LearningPathNavOverviewLearningObjectsComponent { readonly onLearningObjectSelected = output(); constructor() { - effect( - () => { - untracked(() => this.loadLearningObjects()); - }, - { allowSignalWrites: true }, - ); + effect(() => { + untracked(() => this.loadLearningObjects()); + }); } async loadLearningObjects(): Promise { diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview/learning-path-nav-overview.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview/learning-path-nav-overview.component.ts index 6bf5c49cea7a..746874759930 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview/learning-path-nav-overview.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-nav-overview/learning-path-nav-overview.component.ts @@ -13,7 +13,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-learning-path-nav-overview', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [FontAwesomeModule, CommonModule, NgbDropdownModule, NgbAccordionModule, LearningPathNavOverviewLearningObjectsComponent, TranslateDirective], templateUrl: './learning-path-nav-overview.component.html', @@ -41,13 +40,10 @@ export class LearningPathNavOverviewComponent { readonly currentCompetencyOnPath = computed(() => this.competencies()?.find((competency) => competency.masteryProgress < 1)); constructor() { - effect( - () => { - const learningPathId = this.learningPathId(); - untracked(() => this.loadCompetencies(learningPathId)); - }, - { allowSignalWrites: true }, - ); + effect(() => { + const learningPathId = this.learningPathId(); + untracked(() => this.loadCompetencies(learningPathId)); + }); } async loadCompetencies(learningPathId: number): Promise { diff --git a/src/main/webapp/app/course/learning-paths/components/learning-path-student-nav/learning-path-student-nav.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-path-student-nav/learning-path-student-nav.component.ts index dd938e28bd80..c04025d5dec8 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-path-student-nav/learning-path-student-nav.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-path-student-nav/learning-path-student-nav.component.ts @@ -10,7 +10,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-learning-path-student-nav', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, NgbDropdownModule, NgbAccordionModule, FontAwesomeModule, LearningPathNavOverviewComponent, TranslateDirective], templateUrl: './learning-path-student-nav.component.html', @@ -41,13 +40,10 @@ export class LearningPathNavComponent { readonly isDropdownOpen = signal(false); constructor() { - effect( - () => { - const learningPathId = this.learningPathId(); - untracked(() => this.learningPathNavigationService.loadLearningPathNavigation(learningPathId)); - }, - { allowSignalWrites: true }, - ); + effect(() => { + const learningPathId = this.learningPathId(); + untracked(() => this.learningPathNavigationService.loadLearningPathNavigation(learningPathId)); + }); } async selectLearningObject(selectedLearningObject: LearningPathNavigationObjectDTO, isSuccessor: boolean): Promise { diff --git a/src/main/webapp/app/course/learning-paths/components/learning-paths-analytics/learning-paths-analytics.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-paths-analytics/learning-paths-analytics.component.ts index 6c2678d30c5a..c35168f82977 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-paths-analytics/learning-paths-analytics.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-paths-analytics/learning-paths-analytics.component.ts @@ -9,7 +9,6 @@ import { CommonModule } from '@angular/common'; @Component({ selector: 'jhi-learning-paths-analytics', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [CompetencyGraphComponent, TranslateDirective, CommonModule], templateUrl: './learning-paths-analytics.component.html', @@ -29,13 +28,10 @@ export class LearningPathsAnalyticsComponent { readonly valueSelection = signal(CompetencyGraphNodeValueType.AVERAGE_MASTERY_PROGRESS); constructor() { - effect( - () => { - const courseId = this.courseId(); - untracked(() => this.loadInstructionCompetencyGraph(courseId)); - }, - { allowSignalWrites: true }, - ); + effect(() => { + const courseId = this.courseId(); + untracked(() => this.loadInstructionCompetencyGraph(courseId)); + }); } private async loadInstructionCompetencyGraph(courseId: number): Promise { diff --git a/src/main/webapp/app/course/learning-paths/components/learning-paths-configuration/learning-paths-configuration.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-paths-configuration/learning-paths-configuration.component.ts index 6f2d305f54cc..107c7e493a21 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-paths-configuration/learning-paths-configuration.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-paths-configuration/learning-paths-configuration.component.ts @@ -10,7 +10,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-learning-paths-configuration', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [FontAwesomeModule, ArtemisSharedComponentModule, TranslateDirective], templateUrl: './learning-paths-configuration.component.html', @@ -33,13 +32,10 @@ export class LearningPathsConfigurationComponent { readonly includeAllGradedExercisesEnabled = computed(() => this.learningPathsConfiguration()?.includeAllGradedExercises ?? false); constructor() { - effect( - () => { - const courseId = this.courseId(); - untracked(() => this.loadLearningPathsConfiguration(courseId)); - }, - { allowSignalWrites: true }, - ); + effect(() => { + const courseId = this.courseId(); + untracked(() => this.loadLearningPathsConfiguration(courseId)); + }); } private async loadLearningPathsConfiguration(courseId: number): Promise { diff --git a/src/main/webapp/app/course/learning-paths/components/learning-paths-state/learning-paths-state.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-paths-state/learning-paths-state.component.ts index a8460830ca71..20fbb7a8e094 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-paths-state/learning-paths-state.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-paths-state/learning-paths-state.component.ts @@ -11,7 +11,6 @@ import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-learning-paths-state', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [TranslateDirective, CommonModule, FontAwesomeModule], templateUrl: './learning-paths-state.component.html', @@ -45,13 +44,10 @@ export class LearningPathsStateComponent { readonly learningPathHealthState = computed(() => this.learningPathHealth()?.status ?? []); constructor() { - effect( - () => { - const courseId = this.courseId(); - untracked(() => this.loadLearningPathHealthState(courseId)); - }, - { allowSignalWrites: true }, - ); + effect(() => { + const courseId = this.courseId(); + untracked(() => this.loadLearningPathHealthState(courseId)); + }); } protected async loadLearningPathHealthState(courseId: number): Promise { diff --git a/src/main/webapp/app/course/learning-paths/components/learning-paths-table/learning-paths-table.component.ts b/src/main/webapp/app/course/learning-paths/components/learning-paths-table/learning-paths-table.component.ts index 4fe183acd1a5..4043a22c2035 100644 --- a/src/main/webapp/app/course/learning-paths/components/learning-paths-table/learning-paths-table.component.ts +++ b/src/main/webapp/app/course/learning-paths/components/learning-paths-table/learning-paths-table.component.ts @@ -21,7 +21,6 @@ enum TableColumn { @Component({ selector: 'jhi-learning-paths-table', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgbPaginationModule, NgbTypeaheadModule, FormsModule, FontAwesomeModule, ArtemisSharedModule], templateUrl: './learning-paths-table.component.html', @@ -51,14 +50,11 @@ export class LearningPathsTableComponent { private readonly debounceLoadLearningPaths = BaseApiHttpService.debounce(this.loadLearningPaths.bind(this), 300); constructor() { - effect( - () => { - // Load learning paths whenever the courseId changes - const courseId = this.courseId(); - untracked(() => this.loadLearningPaths(courseId)); - }, - { allowSignalWrites: true }, - ); + effect(() => { + // Load learning paths whenever the courseId changes + const courseId = this.courseId(); + untracked(() => this.loadLearningPaths(courseId)); + }); } private async loadLearningPaths(courseId: number): Promise { diff --git a/src/main/webapp/app/course/learning-paths/pages/learning-path-instructor-page/learning-path-instructor-page.component.ts b/src/main/webapp/app/course/learning-paths/pages/learning-path-instructor-page/learning-path-instructor-page.component.ts index dcd31ceae3a9..7496f4fdd15c 100644 --- a/src/main/webapp/app/course/learning-paths/pages/learning-path-instructor-page/learning-path-instructor-page.component.ts +++ b/src/main/webapp/app/course/learning-paths/pages/learning-path-instructor-page/learning-path-instructor-page.component.ts @@ -1,7 +1,6 @@ import { ChangeDetectionStrategy, Component, computed, effect, inject, signal, untracked } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute } from '@angular/router'; -import { LearningPathsConfigurationComponent } from 'app/course/learning-paths/components/learning-paths-configuration/learning-paths-configuration.component'; import { lastValueFrom, map } from 'rxjs'; import { LearningPathApiService } from 'app/course/learning-paths/services/learning-path-api.service'; import { AlertService } from 'app/core/util/alert.service'; @@ -15,9 +14,8 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-learning-path-instructor-page', - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, - imports: [LearningPathsConfigurationComponent, LearningPathsStateComponent, LearningPathsTableComponent, LearningPathsAnalyticsComponent, TranslateDirective], + imports: [LearningPathsStateComponent, LearningPathsTableComponent, LearningPathsAnalyticsComponent, TranslateDirective], templateUrl: './learning-path-instructor-page.component.html', styleUrl: './learning-path-instructor-page.component.scss', }) @@ -34,13 +32,10 @@ export class LearningPathInstructorPageComponent { readonly isLoading = signal(false); constructor() { - effect( - () => { - const courseId = this.courseId(); - untracked(() => this.loadCourse(courseId)); - }, - { allowSignalWrites: true }, - ); + effect(() => { + const courseId = this.courseId(); + untracked(() => this.loadCourse(courseId)); + }); } private async loadCourse(courseId: number): Promise { diff --git a/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts b/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts index 72c77fbf2d40..bf5f11f1f219 100644 --- a/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts +++ b/src/main/webapp/app/course/learning-paths/pages/learning-path-student-page/learning-path-student-page.component.ts @@ -17,7 +17,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; templateUrl: './learning-path-student-page.component.html', styleUrl: './learning-path-student-page.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, - standalone: true, imports: [LearningPathNavComponent, LearningPathLectureUnitComponent, LearningPathExerciseComponent, TranslateDirective], }) export class LearningPathStudentPageComponent { @@ -35,13 +34,10 @@ export class LearningPathStudentPageComponent { readonly isLearningPathNavigationLoading = this.learningPathNavigationService.isLoading; constructor() { - effect( - () => { - const courseId = this.courseId(); - untracked(() => this.loadLearningPath(courseId)); - }, - { allowSignalWrites: true }, - ); + effect(() => { + const courseId = this.courseId(); + untracked(() => this.loadLearningPath(courseId)); + }); } private async loadLearningPath(courseId: number): Promise { diff --git a/src/main/webapp/app/course/manage/course-admin.service.ts b/src/main/webapp/app/course/manage/course-admin.service.ts index 1185c439a8d2..fb28f13466dc 100644 --- a/src/main/webapp/app/course/manage/course-admin.service.ts +++ b/src/main/webapp/app/course/manage/course-admin.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -12,12 +12,10 @@ export type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class CourseAdminService { - private resourceUrl = 'api/admin/courses'; + private http = inject(HttpClient); + private courseManagementService = inject(CourseManagementService); - constructor( - private http: HttpClient, - private courseManagementService: CourseManagementService, - ) {} + private resourceUrl = 'api/admin/courses'; /** * finds all groups for all courses using a GET request diff --git a/src/main/webapp/app/course/manage/course-exercise-card.component.ts b/src/main/webapp/app/course/manage/course-exercise-card.component.ts index b2fbbff8b5b1..85a1e3842dbd 100644 --- a/src/main/webapp/app/course/manage/course-exercise-card.component.ts +++ b/src/main/webapp/app/course/manage/course-exercise-card.component.ts @@ -1,11 +1,15 @@ import { Component, Input } from '@angular/core'; import { faAngleDown, faAngleRight } from '@fortawesome/free-solid-svg-icons'; import { Course } from 'app/entities/course.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbCollapse } from '@ng-bootstrap/ng-bootstrap'; @Component({ selector: 'jhi-course-exercise-card', templateUrl: './course-exercise-card.component.html', styleUrls: ['./course-exercise-card.component.scss', '../../exercises/quiz/shared/quiz.scss'], + imports: [TranslateDirective, FaIconComponent, NgbCollapse], }) export class CourseExerciseCardComponent { @Input() headingJhiTranslate: string; diff --git a/src/main/webapp/app/course/manage/course-for-dashboard-dto.ts b/src/main/webapp/app/course/manage/course-for-dashboard-dto.ts index 67cc0c6bed68..322557637962 100644 --- a/src/main/webapp/app/course/manage/course-for-dashboard-dto.ts +++ b/src/main/webapp/app/course/manage/course-for-dashboard-dto.ts @@ -13,13 +13,10 @@ export class CourseForDashboardDTO { quizScores: CourseScores; participationResults: ParticipationResultDTO[]; - - constructor() {} } export class ParticipationResultDTO { score?: number; rated?: boolean; participationId: number; - constructor() {} } diff --git a/src/main/webapp/app/course/manage/course-group-membership/course-group-membership.component.ts b/src/main/webapp/app/course/manage/course-group-membership/course-group-membership.component.ts index 0b9626661fa4..4e605fc6d9a1 100644 --- a/src/main/webapp/app/course/manage/course-group-membership/course-group-membership.component.ts +++ b/src/main/webapp/app/course/manage/course-group-membership/course-group-membership.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { Course, CourseGroup, courseGroups } from 'app/entities/course.model'; import { User } from 'app/core/user/user.model'; import { AccountService } from 'app/core/auth/account.service'; @@ -9,12 +9,23 @@ import { CourseManagementService } from 'app/course/manage/course-management.ser import { UserService } from 'app/core/user/user.service'; import { Subscription } from 'rxjs'; import { capitalize } from 'lodash-es'; +import { CourseGroupComponent } from 'app/shared/course-group/course-group.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-course-group-membership', templateUrl: './course-group-membership.component.html', + imports: [CourseGroupComponent, TranslateDirective], }) export class CourseGroupMembershipComponent implements OnInit { + private router = inject(Router); + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + private eventManager = inject(EventManager); + private courseService = inject(CourseManagementService); + private userService = inject(UserService); + private accountService = inject(AccountService); + allCourseGroupUsers: User[] = []; course: Course; courseGroup: CourseGroup; @@ -25,16 +36,6 @@ export class CourseGroupMembershipComponent implements OnInit { readonly capitalize = capitalize; - constructor( - private router: Router, - private route: ActivatedRoute, - private alertService: AlertService, - private eventManager: EventManager, - private courseService: CourseManagementService, - private userService: UserService, - private accountService: AccountService, - ) {} - ngOnInit(): void { this.loadAll(); } diff --git a/src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.ts b/src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.ts index d91fcf8469b2..cdcc305d2a82 100644 --- a/src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.ts +++ b/src/main/webapp/app/course/manage/course-lti-configuration/course-lti-configuration.component.ts @@ -7,17 +7,16 @@ import { Exercise } from 'app/entities/exercise.model'; import { faExclamationTriangle, faSort, faWrench } from '@fortawesome/free-solid-svg-icons'; import { SortService } from 'app/shared/service/sort.service'; import { FormsModule } from '@angular/forms'; -import { TranslateDirective } from '../../../shared/language/translate.directive'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { NgbNav, NgbNavContent, NgbNavItem, NgbNavLink, NgbNavLinkBase, NgbNavOutlet, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; -import { ArtemisSharedComponentModule } from '../../../shared/components/shared-component.module'; -import { ArtemisSharedCommonModule } from '../../../shared/shared-common.module'; -import { ArtemisTranslatePipe } from '../../../shared/pipes/artemis-translate.pipe'; +import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; +import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-course-lti-configuration', templateUrl: './course-lti-configuration.component.html', - standalone: true, imports: [ FormsModule, TranslateDirective, diff --git a/src/main/webapp/app/course/manage/course-lti-configuration/edit-course-lti-configuration.component.ts b/src/main/webapp/app/course/manage/course-lti-configuration/edit-course-lti-configuration.component.ts index 78f46c1f410d..28d581d5e346 100644 --- a/src/main/webapp/app/course/manage/course-lti-configuration/edit-course-lti-configuration.component.ts +++ b/src/main/webapp/app/course/manage/course-lti-configuration/edit-course-lti-configuration.component.ts @@ -1,6 +1,7 @@ import { Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Course } from 'app/entities/course.model'; +import { HasAnyAuthorityDirective } from 'app/shared/auth/has-any-authority.directive'; import { finalize } from 'rxjs'; import { OnlineCourseConfiguration } from 'app/entities/online-course-configuration.model'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -13,19 +14,18 @@ import { LtiConfigurationService } from 'app/admin/lti-configuration/lti-configu import { ITEMS_PER_PAGE } from 'app/shared/constants/pagination.constants'; import { HttpHeaders, HttpResponse } from '@angular/common/http'; import { combineLatest } from 'rxjs'; -import { TranslateDirective } from '../../../shared/language/translate.directive'; -import { ArtemisSharedComponentModule } from '../../../shared/components/shared-component.module'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { NgbDropdown, NgbDropdownButtonItem, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle, NgbPagination, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { KeyValuePipe } from '@angular/common'; -import { ArtemisTranslatePipe } from '../../../shared/pipes/artemis-translate.pipe'; -import { ArtemisSharedPipesModule } from '../../../shared/pipes/shared-pipes.module'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisSharedPipesModule } from 'app/shared/pipes/shared-pipes.module'; @Component({ selector: 'jhi-edit-course-lti-configuration', templateUrl: './edit-course-lti-configuration.component.html', - standalone: true, imports: [ FormsModule, ReactiveFormsModule, @@ -43,6 +43,8 @@ import { ArtemisSharedPipesModule } from '../../../shared/pipes/shared-pipes.mod KeyValuePipe, ArtemisTranslatePipe, ArtemisSharedPipesModule, + // NOTE: this is actually used in the html template, otherwise *jhiHasAnyAuthority would not work + HasAnyAuthorityDirective, ], }) export class EditCourseLtiConfigurationComponent implements OnInit { diff --git a/src/main/webapp/app/course/manage/course-management-detail-view-dto.model.ts b/src/main/webapp/app/course/manage/course-management-detail-view-dto.model.ts index e262f717a7de..4e8983fe099f 100644 --- a/src/main/webapp/app/course/manage/course-management-detail-view-dto.model.ts +++ b/src/main/webapp/app/course/manage/course-management-detail-view-dto.model.ts @@ -28,6 +28,4 @@ export class CourseManagementDetailViewDto { // LLM Stats currentTotalLlmCostInEur: number; - - constructor() {} } diff --git a/src/main/webapp/app/course/manage/course-management-exercises-search.component.ts b/src/main/webapp/app/course/manage/course-management-exercises-search.component.ts index aeb057ef6e2a..bbc433ad8aba 100644 --- a/src/main/webapp/app/course/manage/course-management-exercises-search.component.ts +++ b/src/main/webapp/app/course/manage/course-management-exercises-search.component.ts @@ -1,11 +1,15 @@ import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { ExerciseFilter } from 'app/entities/exercise-filter.model'; import { exerciseTypes } from 'app/entities/exercise.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-course-management-exercises-search', templateUrl: './course-management-exercises-search.component.html', styleUrls: ['./course-management-exercises-search.component.scss'], + imports: [TranslateDirective, FormsModule, ArtemisTranslatePipe], }) export class CourseManagementExercisesSearchComponent implements OnInit { typeOptions: string[]; diff --git a/src/main/webapp/app/course/manage/course-management-exercises.component.html b/src/main/webapp/app/course/manage/course-management-exercises.component.html index 51f7c1ecfb10..176580fff610 100644 --- a/src/main/webapp/app/course/manage/course-management-exercises.component.html +++ b/src/main/webapp/app/course/manage/course-management-exercises.component.html @@ -27,19 +27,32 @@

    [hidden]="shouldHideExerciseCard('programming')" [course]="course" > - + + + + + + + + + + + + + + + + + + + + + + + + (filteredExerciseCount)="filteredProgrammingExercisesCount = $event" /> + + + + + + + + (filteredExerciseCount)="filteredQuizExercisesCount = $event" /> + + + + + + + + (filteredExerciseCount)="filteredModelingExercisesCount = $event" /> + + + + + + + + (filteredExerciseCount)="filteredTextExercisesCount = $event" /> + + + + + + + + ; - @ContentChild('overrideProgrammingExerciseCard') overrideProgrammingExerciseCard: TemplateRef; - @ContentChild('overrideNonProgrammingExerciseCard') overrideNonProgrammingExerciseCard: TemplateRef; + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // @ContentChild('overrideGenerateAndImportButton') overrideGenerateAndImportButton: TemplateRef; + // @ContentChild('overrideProgrammingExerciseCard') overrideProgrammingExerciseCard: TemplateRef; + // @ContentChild('overrideNonProgrammingExerciseCard') overrideNonProgrammingExerciseCard: TemplateRef; private readonly route = inject(ActivatedRoute); diff --git a/src/main/webapp/app/course/manage/course-management-resolve.service.ts b/src/main/webapp/app/course/manage/course-management-resolve.service.ts index 1e789622ccbb..ebebf754ba8f 100644 --- a/src/main/webapp/app/course/manage/course-management-resolve.service.ts +++ b/src/main/webapp/app/course/manage/course-management-resolve.service.ts @@ -1,13 +1,13 @@ import { Course } from 'app/entities/course.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable, filter, map, of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class CourseManagementResolve implements Resolve { - constructor(private service: CourseManagementService) {} + private service = inject(CourseManagementService); /** * Resolves the route by extracting the courseId and returns the course with that Id if it exists diff --git a/src/main/webapp/app/course/manage/course-management-statistics-dto.ts b/src/main/webapp/app/course/manage/course-management-statistics-dto.ts index adc705057731..3aba5cd52140 100644 --- a/src/main/webapp/app/course/manage/course-management-statistics-dto.ts +++ b/src/main/webapp/app/course/manage/course-management-statistics-dto.ts @@ -3,6 +3,4 @@ import { CourseManagementStatisticsModel } from 'app/entities/quiz/course-manage export class CourseManagementStatisticsDTO { averageScoreOfCourse: number; averageScoresOfExercises: CourseManagementStatisticsModel[]; - - constructor() {} } diff --git a/src/main/webapp/app/course/manage/course-management-statistics.component.ts b/src/main/webapp/app/course/manage/course-management-statistics.component.ts index 4b2dbf79b37d..516dc03f2555 100644 --- a/src/main/webapp/app/course/manage/course-management-statistics.component.ts +++ b/src/main/webapp/app/course/manage/course-management-statistics.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Graphs, SpanType, StatisticsView } from 'app/entities/statistics.model'; import { Subscription } from 'rxjs'; @@ -6,13 +6,22 @@ import { StatisticsService } from 'app/shared/statistics-graph/statistics.servic import { CourseManagementStatisticsDTO } from './course-management-statistics-dto'; import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component'; import { Course, isCommunicationEnabled } from 'app/entities/course.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { StatisticsAverageScoreGraphComponent } from 'app/shared/statistics-graph/statistics-average-score-graph.component'; +import { StatisticsGraphComponent } from 'app/shared/statistics-graph/statistics-graph.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-course-management-statistics', templateUrl: './course-management-statistics.component.html', styleUrls: ['./course-management-statistics.component.scss'], + imports: [TranslateDirective, DocumentationButtonComponent, StatisticsAverageScoreGraphComponent, StatisticsGraphComponent, ArtemisTranslatePipe], }) export class CourseManagementStatisticsComponent implements OnInit { + private service = inject(StatisticsService); + private route = inject(ActivatedRoute); + readonly documentationType: DocumentationType = 'Statistics'; // html properties SpanType = SpanType; @@ -40,11 +49,6 @@ export class CourseManagementStatisticsComponent implements OnInit { courseStatistics: CourseManagementStatisticsDTO; - constructor( - private service: StatisticsService, - private route: ActivatedRoute, - ) {} - ngOnInit() { this.paramSub = this.route.params.subscribe((params) => { this.courseId = params['courseId']; diff --git a/src/main/webapp/app/course/manage/course-management-tab-bar/course-management-tab-bar.component.ts b/src/main/webapp/app/course/manage/course-management-tab-bar/course-management-tab-bar.component.ts index 8e15c55fbd28..ef7b8075ba02 100644 --- a/src/main/webapp/app/course/manage/course-management-tab-bar/course-management-tab-bar.component.ts +++ b/src/main/webapp/app/course/manage/course-management-tab-bar/course-management-tab-bar.component.ts @@ -1,6 +1,7 @@ -import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { AfterViewInit, Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, Router, RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; import { HttpErrorResponse } from '@angular/common/http'; +import { HasAnyAuthorityDirective } from 'app/shared/auth/has-any-authority.directive'; import { Observable, Subject, Subscription, map, of } from 'rxjs'; import { Course, isCommunicationEnabled } from 'app/entities/course.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; @@ -29,20 +30,48 @@ import { } from '@fortawesome/free-solid-svg-icons'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { CourseAdminService } from 'app/course/manage/course-admin.service'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { PROFILE_IRIS, PROFILE_LOCALCI, PROFILE_LTI } from 'app/app.constants'; import { CourseAccessStorageService } from 'app/course/course-access-storage.service'; import { scrollToTopOfPage } from 'app/shared/util/utils'; import { ExerciseType } from 'app/entities/exercise.model'; import { EntitySummary } from 'app/shared/delete-dialog/delete-dialog.model'; +import { HeaderCourseComponent } from 'app/overview/header-course.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FeatureToggleLinkDirective } from 'app/shared/feature-toggle/feature-toggle-link.directive'; +import { FeatureToggleHideDirective } from 'app/shared/feature-toggle/feature-toggle-hide.directive'; +import { CourseExamArchiveButtonComponent } from 'app/shared/components/course-exam-archive-button/course-exam-archive-button.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; @Component({ selector: 'jhi-course-management-tab-bar', templateUrl: './course-management-tab-bar.component.html', styleUrls: ['./course-management-tab-bar.component.scss'], + imports: [ + HeaderCourseComponent, + RouterLinkActive, + RouterLink, + FaIconComponent, + TranslateDirective, + FeatureToggleLinkDirective, + FeatureToggleHideDirective, + CourseExamArchiveButtonComponent, + DeleteButtonDirective, + RouterOutlet, + // NOTE: this is actually used in the html template, otherwise *jhiHasAnyAuthority would not work + HasAnyAuthorityDirective, + ], }) export class CourseManagementTabBarComponent implements OnInit, OnDestroy, AfterViewInit { + private eventManager = inject(EventManager); + private courseManagementService = inject(CourseManagementService); + private courseAdminService = inject(CourseAdminService); + private route = inject(ActivatedRoute); + private router = inject(Router); + private profileService = inject(ProfileService); + private courseAccessStorageService = inject(CourseAccessStorageService); + readonly FeatureToggle = FeatureToggle; readonly ButtonSize = ButtonSize; @@ -52,7 +81,7 @@ export class CourseManagementTabBarComponent implements OnInit, OnDestroy, After private courseSub?: Subscription; private eventSubscriber: Subscription; - localCIActive: boolean = false; + localCIActive = false; private dialogErrorSource = new Subject(); dialogError$ = this.dialogErrorSource.asObservable(); @@ -83,17 +112,6 @@ export class CourseManagementTabBarComponent implements OnInit, OnDestroy, After irisEnabled = false; ltiEnabled = false; - constructor( - private eventManager: EventManager, - private courseManagementService: CourseManagementService, - private courseAdminService: CourseAdminService, - private route: ActivatedRoute, - private router: Router, - private modalService: NgbModal, - private profileService: ProfileService, - private courseAccessStorageService: CourseAccessStorageService, - ) {} - /** * On init load the course information and subscribe to listen for changes in course. */ diff --git a/src/main/webapp/app/course/manage/course-management.component.ts b/src/main/webapp/app/course/manage/course-management.component.ts index e6706bd2b2ee..f5970f30cba4 100644 --- a/src/main/webapp/app/course/manage/course-management.component.ts +++ b/src/main/webapp/app/course/manage/course-management.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; +import { AfterViewInit, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { Subject, Subscription } from 'rxjs'; import { Course } from 'app/entities/course.model'; @@ -12,14 +12,39 @@ import { EventManager } from 'app/core/util/event-manager.service'; import { faAngleDown, faAngleUp, faPlus } from '@fortawesome/free-solid-svg-icons'; import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component'; import { CourseAccessStorageService } from 'app/course/course-access-storage.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { HasAnyAuthorityDirective } from 'app/shared/auth/has-any-authority.directive'; +import { OrionFilterDirective } from 'app/shared/orion/orion-filter.directive'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { CourseManagementCardComponent } from './overview/course-management-card.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-course', templateUrl: './course-management.component.html', styles: ['.course-table {padding-bottom: 5rem}'], styleUrls: ['./course-management.component.scss'], + imports: [ + TranslateDirective, + DocumentationButtonComponent, + // NOTE: this is actually used in the html template, otherwise *jhiHasAnyAuthority would not work + HasAnyAuthorityDirective, + OrionFilterDirective, + RouterLink, + FaIconComponent, + CourseManagementCardComponent, + ArtemisTranslatePipe, + ], }) export class CourseManagementComponent implements OnInit, OnDestroy, AfterViewInit { + private courseManagementService = inject(CourseManagementService); + private alertService = inject(AlertService); + private eventManager = inject(EventManager); + private guidedTourService = inject(GuidedTourService); + private courseAccessStorageService = inject(CourseAccessStorageService); + showOnlyActive = true; courses: Course[]; @@ -42,14 +67,6 @@ export class CourseManagementComponent implements OnInit, OnDestroy, AfterViewIn faAngleDown = faAngleDown; faAngleUp = faAngleUp; - constructor( - private courseManagementService: CourseManagementService, - private alertService: AlertService, - private eventManager: EventManager, - private guidedTourService: GuidedTourService, - private courseAccessStorageService: CourseAccessStorageService, - ) {} - /** * loads all courses and subscribes to courseListModification */ diff --git a/src/main/webapp/app/course/manage/course-management.module.ts b/src/main/webapp/app/course/manage/course-management.module.ts index 3aeca48c59d3..bf0afc89a1d2 100644 --- a/src/main/webapp/app/course/manage/course-management.module.ts +++ b/src/main/webapp/app/course/manage/course-management.module.ts @@ -49,11 +49,11 @@ import { CourseManagementExercisesSearchComponent } from 'app/course/manage/cour import { LineChartModule, PieChartModule } from '@swimlane/ngx-charts'; import { ArtemisPlagiarismModule } from 'app/exercises/shared/plagiarism/plagiarism.module'; import { ArtemisChartsModule } from 'app/shared/chart/artemis-charts.module'; -import { ImageCropperModule } from 'app/shared/image-cropper/image-cropper.module'; + import { ArtemisFullscreenModule } from 'app/shared/fullscreen/fullscreen.module'; import { ArtemisCourseGroupModule } from 'app/shared/course-group/course-group.module'; import { CourseGroupMembershipComponent } from './course-group-membership/course-group-membership.component'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { CourseLtiConfigurationComponent } from 'app/course/manage/course-lti-configuration/course-lti-configuration.component'; import { EditCourseLtiConfigurationComponent } from 'app/course/manage/course-lti-configuration/edit-course-lti-configuration.component'; import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'; @@ -75,7 +75,6 @@ import { ComplaintsForTutorComponent } from 'app/complaints/complaints-for-tutor RouterModule.forChild(courseManagementState), FormDateTimePickerModule, ReactiveFormsModule, - ImageCropperModule, OrionModule, MatChipsModule, ArtemisExerciseModule, @@ -112,7 +111,6 @@ import { ComplaintsForTutorComponent } from 'app/complaints/complaints-for-tutor ArtemisPlagiarismModule, ArtemisChartsModule, ArtemisCourseGroupModule, - FeatureToggleModule, ExerciseCategoriesModule, NgbNavModule, ArtemisExerciseCreateButtonsModule, @@ -122,8 +120,6 @@ import { ComplaintsForTutorComponent } from 'app/complaints/complaints-for-tutor ArtemisMarkdownEditorModule, CourseLtiConfigurationComponent, EditCourseLtiConfigurationComponent, - ], - declarations: [ CourseManagementComponent, CourseDetailComponent, CourseUpdateComponent, diff --git a/src/main/webapp/app/course/manage/course-management.route.ts b/src/main/webapp/app/course/manage/course-management.route.ts index 9d00cb7cc48f..cf0b92a77ecc 100644 --- a/src/main/webapp/app/course/manage/course-management.route.ts +++ b/src/main/webapp/app/course/manage/course-management.route.ts @@ -1,46 +1,17 @@ import { Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { CourseManagementComponent } from './course-management.component'; -import { CourseDetailComponent } from './detail/course-detail.component'; -import { CourseUpdateComponent } from './course-update.component'; -import { CourseManagementExercisesComponent } from './course-management-exercises.component'; import { Authority } from 'app/shared/constants/authority.constants'; -import { RatingListComponent } from 'app/exercises/shared/rating/rating-list/rating-list.component'; -import { CompetencyManagementComponent } from 'app/course/competencies/competency-management/competency-management.component'; -import { CreateCompetencyComponent } from 'app/course/competencies/create/create-competency.component'; -import { EditCompetencyComponent } from 'app/course/competencies/edit/edit-competency.component'; -import { GenerateCompetenciesComponent } from 'app/course/competencies/generate-competencies/generate-competencies.component'; -import { CourseManagementStatisticsComponent } from './course-management-statistics.component'; -import { GradingSystemComponent } from 'app/grading-system/grading-system.component'; -import { isOrion } from 'app/shared/orion/orion'; -import { OrionCourseManagementExercisesComponent } from 'app/orion/management/orion-course-management-exercises.component'; import { CourseManagementResolve } from 'app/course/manage/course-management-resolve.service'; -import { CourseGroupMembershipComponent } from 'app/course/manage/course-group-membership/course-group-membership.component'; import { TutorialGroupManagementResolve } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-group-management-resolve.service'; -import { TutorialGroupsChecklistComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-checklist/tutorial-groups-checklist.component'; -import { CreateTutorialGroupsConfigurationComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/create-tutorial-groups-configuration/create-tutorial-groups-configuration.component'; -import { CourseLtiConfigurationComponent } from 'app/course/manage/course-lti-configuration/course-lti-configuration.component'; -import { EditCourseLtiConfigurationComponent } from 'app/course/manage/course-lti-configuration/edit-course-lti-configuration.component'; -import { CourseManagementTabBarComponent } from 'app/course/manage/course-management-tab-bar/course-management-tab-bar.component'; import { PendingChangesGuard } from 'app/shared/guard/pending-changes.guard'; -import { BuildQueueComponent } from 'app/localci/build-queue/build-queue.component'; -import { ImportCompetenciesComponent } from 'app/course/competencies/import/import-competencies.component'; import { LocalCIGuard } from 'app/localci/localci-guard.service'; import { IrisGuard } from 'app/iris/iris-guard.service'; -import { CourseImportStandardizedCompetenciesComponent } from 'app/course/competencies/import-standardized-competencies/course-import-standardized-competencies.component'; -import { ImportPrerequisitesComponent } from 'app/course/competencies/import/import-prerequisites.component'; -import { CreatePrerequisiteComponent } from 'app/course/competencies/create/create-prerequisite.component'; -import { EditPrerequisiteComponent } from 'app/course/competencies/edit/edit-prerequisite.component'; -import { CourseImportStandardizedPrerequisitesComponent } from 'app/course/competencies/import-standardized-competencies/course-import-standardized-prerequisites.component'; -import { LearningPathInstructorPageComponent } from 'app/course/learning-paths/pages/learning-path-instructor-page/learning-path-instructor-page.component'; -import { FaqComponent } from 'app/faq/faq.component'; -import { FaqUpdateComponent } from 'app/faq/faq-update.component'; import { FaqResolve } from 'app/faq/faq.routes'; export const courseManagementState: Routes = [ { path: '', - component: CourseManagementComponent, + loadComponent: () => import('./course-management.component').then((m) => m.CourseManagementComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.course.home.title', @@ -49,7 +20,7 @@ export const courseManagementState: Routes = [ }, { path: 'new', - component: CourseUpdateComponent, + loadComponent: () => import('./course-update.component').then((m) => m.CourseUpdateComponent), data: { authorities: [Authority.ADMIN], pageTitle: 'global.generic.create', @@ -58,11 +29,11 @@ export const courseManagementState: Routes = [ }, { path: '', - component: CourseManagementTabBarComponent, + loadComponent: () => import('app/course/manage/course-management-tab-bar/course-management-tab-bar.component').then((m) => m.CourseManagementTabBarComponent), children: [ { path: ':courseId', - component: CourseDetailComponent, + loadComponent: () => import('./detail/course-detail.component').then((m) => m.CourseDetailComponent), resolve: { course: CourseManagementResolve, }, @@ -74,7 +45,7 @@ export const courseManagementState: Routes = [ }, { path: ':courseId/grading-system', - component: GradingSystemComponent, + loadComponent: () => import('app/grading-system/grading-system.component').then((m) => m.GradingSystemComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.course.gradingSystem', @@ -109,7 +80,10 @@ export const courseManagementState: Routes = [ }, { path: ':courseId/tutorial-groups-checklist', - component: TutorialGroupsChecklistComponent, + loadComponent: () => + import('app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-checklist/tutorial-groups-checklist.component').then( + (m) => m.TutorialGroupsChecklistComponent, + ), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.pages.checklist.title', @@ -118,7 +92,10 @@ export const courseManagementState: Routes = [ }, { path: ':courseId/create-tutorial-groups-configuration', - component: CreateTutorialGroupsConfigurationComponent, + loadComponent: () => + import( + 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/create-tutorial-groups-configuration/create-tutorial-groups-configuration.component' + ).then((m) => m.CreateTutorialGroupsConfigurationComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.pages.createTutorialGroupsConfiguration.title', @@ -127,7 +104,7 @@ export const courseManagementState: Routes = [ }, { path: ':courseId/lti-configuration', - component: CourseLtiConfigurationComponent, + loadComponent: () => import('app/course/manage/course-lti-configuration/course-lti-configuration.component').then((m) => m.CourseLtiConfigurationComponent), resolve: { course: CourseManagementResolve, }, @@ -139,7 +116,8 @@ export const courseManagementState: Routes = [ }, { path: ':courseId/lti-configuration/edit', - component: EditCourseLtiConfigurationComponent, + loadComponent: () => + import('app/course/manage/course-lti-configuration/edit-course-lti-configuration.component').then((m) => m.EditCourseLtiConfigurationComponent), resolve: { course: CourseManagementResolve, }, @@ -158,7 +136,7 @@ export const courseManagementState: Routes = [ children: [ { path: 'exercises', - component: !isOrion ? CourseManagementExercisesComponent : OrionCourseManagementExercisesComponent, + loadComponent: () => import('app/orion/management/orion-course-management-exercises.component').then((m) => m.OrionCourseManagementExercisesComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.course.exercises', @@ -167,7 +145,7 @@ export const courseManagementState: Routes = [ }, { path: 'course-statistics', - component: CourseManagementStatisticsComponent, + loadComponent: () => import('./course-management-statistics.component').then((m) => m.CourseManagementStatisticsComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.courseStatistics.statistics', @@ -177,7 +155,7 @@ export const courseManagementState: Routes = [ }, { path: 'edit', - component: CourseUpdateComponent, + loadComponent: () => import('./course-update.component').then((m) => m.CourseUpdateComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.course.home.editLabel', @@ -186,7 +164,7 @@ export const courseManagementState: Routes = [ }, { path: 'groups/:courseGroup', - component: CourseGroupMembershipComponent, + loadComponent: () => import('app/course/manage/course-group-membership/course-group-membership.component').then((m) => m.CourseGroupMembershipComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.userManagement.groups', @@ -195,7 +173,7 @@ export const courseManagementState: Routes = [ }, { path: 'ratings', - component: RatingListComponent, + loadComponent: () => import('app/exercises/shared/rating/rating-list/rating-list.component').then((m) => m.RatingListComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.ratingList.pageTitle', @@ -204,7 +182,7 @@ export const courseManagementState: Routes = [ }, { path: 'competency-management', - component: CompetencyManagementComponent, + loadComponent: () => import('app/course/competencies/competency-management/competency-management.component').then((m) => m.CompetencyManagementComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.competency.manage.title', @@ -220,7 +198,7 @@ export const courseManagementState: Routes = [ children: [ { path: 'create', - component: CreateCompetencyComponent, + loadComponent: () => import('app/course/competencies/create/create-competency.component').then((m) => m.CreateCompetencyComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.competency.create.title', @@ -229,7 +207,7 @@ export const courseManagementState: Routes = [ }, { path: ':competencyId/edit', - component: EditCompetencyComponent, + loadComponent: () => import('app/course/competencies/edit/edit-competency.component').then((m) => m.EditCompetencyComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.competency.editCompetency.title', @@ -238,7 +216,7 @@ export const courseManagementState: Routes = [ }, { path: 'import', - component: ImportCompetenciesComponent, + loadComponent: () => import('app/course/competencies/import/import-competencies.component').then((m) => m.ImportCompetenciesComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.competency.import.title', @@ -248,7 +226,10 @@ export const courseManagementState: Routes = [ }, { path: 'import-standardized', - component: CourseImportStandardizedCompetenciesComponent, + loadComponent: () => + import('app/course/competencies/import-standardized-competencies/course-import-standardized-competencies.component').then( + (m) => m.CourseImportStandardizedCompetenciesComponent, + ), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.competency.import.title', @@ -258,7 +239,8 @@ export const courseManagementState: Routes = [ }, { path: 'generate', - component: GenerateCompetenciesComponent, + loadComponent: () => + import('app/course/competencies/generate-competencies/generate-competencies.component').then((m) => m.GenerateCompetenciesComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.competency.generate.title', @@ -281,7 +263,7 @@ export const courseManagementState: Routes = [ children: [ { path: 'create', - component: CreatePrerequisiteComponent, + loadComponent: () => import('app/course/competencies/create/create-prerequisite.component').then((m) => m.CreatePrerequisiteComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.prerequisite.createPrerequisite.title', @@ -290,7 +272,7 @@ export const courseManagementState: Routes = [ }, { path: ':prerequisiteId/edit', - component: EditPrerequisiteComponent, + loadComponent: () => import('app/course/competencies/edit/edit-prerequisite.component').then((m) => m.EditPrerequisiteComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.prerequisite.editPrerequisite.title', @@ -299,7 +281,7 @@ export const courseManagementState: Routes = [ }, { path: 'import', - component: ImportPrerequisitesComponent, + loadComponent: () => import('app/course/competencies/import/import-prerequisites.component').then((m) => m.ImportPrerequisitesComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.prerequisite.import.title', @@ -309,7 +291,10 @@ export const courseManagementState: Routes = [ }, { path: 'import-standardized', - component: CourseImportStandardizedPrerequisitesComponent, + loadComponent: () => + import('app/course/competencies/import-standardized-competencies/course-import-standardized-prerequisites.component').then( + (m) => m.CourseImportStandardizedPrerequisitesComponent, + ), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.prerequisite.import.title', @@ -321,7 +306,10 @@ export const courseManagementState: Routes = [ }, { path: 'learning-path-management', - component: LearningPathInstructorPageComponent, + loadComponent: () => + import('app/course/learning-paths/pages/learning-path-instructor-page/learning-path-instructor-page.component').then( + (m) => m.LearningPathInstructorPageComponent, + ), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.learningPath.manageLearningPaths.title', @@ -330,7 +318,7 @@ export const courseManagementState: Routes = [ }, { path: 'build-queue', - component: BuildQueueComponent, + loadComponent: () => import('app/localci/build-queue/build-queue.component').then((m) => m.BuildQueueComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.buildQueue.title', @@ -342,7 +330,7 @@ export const courseManagementState: Routes = [ children: [ { path: '', - component: FaqComponent, + loadComponent: () => import('app/faq/faq.component').then((m) => m.FaqComponent), resolve: { course: CourseManagementResolve, }, @@ -361,7 +349,7 @@ export const courseManagementState: Routes = [ children: [ { path: 'new', - component: FaqUpdateComponent, + loadComponent: () => import('app/faq/faq-update.component').then((m) => m.FaqUpdateComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'global.generic.create', @@ -376,7 +364,7 @@ export const courseManagementState: Routes = [ children: [ { path: 'edit', - component: FaqUpdateComponent, + loadComponent: () => import('app/faq/faq-update.component').then((m) => m.FaqUpdateComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'global.generic.edit', diff --git a/src/main/webapp/app/course/manage/course-management.service.ts b/src/main/webapp/app/course/manage/course-management.service.ts index 7aeb99ef0c0b..895d180db81a 100644 --- a/src/main/webapp/app/course/manage/course-management.service.ts +++ b/src/main/webapp/app/course/manage/course-management.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { CoursesForDashboardDTO } from 'app/course/manage/courses-for-dashboard-dto'; import { BehaviorSubject, Observable } from 'rxjs'; @@ -36,6 +36,15 @@ export type RoleGroup = 'tutors' | 'students' | 'instructors' | 'editors'; @Injectable({ providedIn: 'root' }) export class CourseManagementService { + private http = inject(HttpClient); + private courseStorageService = inject(CourseStorageService); + private lectureService = inject(LectureService); + private accountService = inject(AccountService); + private entityTitleService = inject(EntityTitleService); + private tutorialGroupsConfigurationService = inject(TutorialGroupsConfigurationService); + private tutorialGroupsService = inject(TutorialGroupsService); + private scoresStorageService = inject(ScoresStorageService); + private resourceUrl = 'api/courses'; private coursesForNotifications: BehaviorSubject = new BehaviorSubject(undefined); @@ -45,17 +54,6 @@ export class CourseManagementService { private courseOverviewSubject = new BehaviorSubject(false); isCourseOverview$ = this.courseOverviewSubject.asObservable(); - constructor( - private http: HttpClient, - private courseStorageService: CourseStorageService, - private lectureService: LectureService, - private accountService: AccountService, - private entityTitleService: EntityTitleService, - private tutorialGroupsConfigurationService: TutorialGroupsConfigurationService, - private tutorialGroupsService: TutorialGroupsService, - private scoresStorageService: ScoresStorageService, - ) {} - /** * updates a course using a PUT request * @param courseId - the id of the course to be updated diff --git a/src/main/webapp/app/course/manage/course-update.component.ts b/src/main/webapp/app/course/manage/course-update.component.ts index 8922497168dd..5fcde16d3efb 100644 --- a/src/main/webapp/app/course/manage/course-update.component.ts +++ b/src/main/webapp/app/course/manage/course-update.component.ts @@ -1,8 +1,9 @@ import { ActivatedRoute, Router } from '@angular/router'; import { Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core'; -import { FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, ValidatorFn, Validators } from '@angular/forms'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { AlertService, AlertType } from 'app/core/util/alert.service'; +import { HasAnyAuthorityDirective } from 'app/shared/auth/has-any-authority.directive'; import { Observable, OperatorFunction, Subject, debounceTime, distinctUntilChanged, filter, map, merge, tap } from 'rxjs'; import { regexValidator } from 'app/shared/form/shortname-validator.directive'; import { Course, CourseInformationSharingConfiguration, isCommunicationEnabled, isMessagingEnabled } from 'app/entities/course.model'; @@ -15,12 +16,11 @@ import dayjs from 'dayjs/esm'; import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; import { SHORT_NAME_PATTERN } from 'app/shared/constants/input.constants'; import { Organization } from 'app/entities/organization.model'; -import { NgbModal, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; import { OrganizationManagementService } from 'app/admin/organization-management/organization-management.service'; import { OrganizationSelectorComponent } from 'app/shared/organization-selector/organization-selector.component'; import { faBan, faExclamationTriangle, faPen, faQuestionCircle, faSave, faTimes, faTrash } from '@fortawesome/free-solid-svg-icons'; import { base64StringToBlob } from 'app/utils/blob-util'; -import { ImageCroppedEvent } from 'app/shared/image-cropper/interfaces/image-cropped-event.interface'; import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { CourseAdminService } from 'app/course/manage/course-admin.service'; import { FeatureToggle, FeatureToggleService } from 'app/shared/feature-toggle/feature-toggle.service'; @@ -32,6 +32,16 @@ import { getSemesters } from 'app/utils/semester-utils'; import { ImageCropperModalComponent } from 'app/course/manage/image-cropper-modal.component'; import { scrollToTopOfPage } from 'app/shared/util/utils'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; +import { SecuredImageComponent } from 'app/shared/image/secured-image.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { KeyValuePipe, NgClass, NgStyle, NgTemplateOutlet } from '@angular/common'; +import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { FeatureToggleHideDirective } from 'app/shared/feature-toggle/feature-toggle-hide.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { RemoveKeysPipe } from 'app/shared/pipes/remove-keys.pipe'; const DEFAULT_CUSTOM_GROUP_NAME = 'artemis-dev'; @@ -39,20 +49,58 @@ const DEFAULT_CUSTOM_GROUP_NAME = 'artemis-dev'; selector: 'jhi-course-update', templateUrl: './course-update.component.html', styleUrls: ['./course-update.component.scss'], + imports: [ + FormsModule, + ReactiveFormsModule, + SecuredImageComponent, + FaIconComponent, + TranslateDirective, + NgStyle, + ColorSelectorComponent, + NgClass, + NgbTooltip, + FormDateTimePickerComponent, + HelpIconComponent, + MarkdownEditorMonacoComponent, + FeatureToggleHideDirective, + NgbTypeahead, + NgTemplateOutlet, + KeyValuePipe, + ArtemisTranslatePipe, + RemoveKeysPipe, + // NOTE: this is actually used in the html template, otherwise *jhiHasAnyAuthority would not work + HasAnyAuthorityDirective, + ], }) export class CourseUpdateComponent implements OnInit { + private eventManager = inject(EventManager); + private courseManagementService = inject(CourseManagementService); + private courseAdminService = inject(CourseAdminService); + private activatedRoute = inject(ActivatedRoute); + private fileService = inject(FileService); + private alertService = inject(AlertService); + private profileService = inject(ProfileService); + private organizationService = inject(OrganizationManagementService); + private modalService = inject(NgbModal); + private navigationUtilService = inject(ArtemisNavigationUtilService); + private router = inject(Router); + private featureToggleService = inject(FeatureToggleService); + private accountService = inject(AccountService); + CachingStrategy = CachingStrategy; ProgrammingLanguage = ProgrammingLanguage; + @ViewChild('fileInput', { static: false }) fileInput: ElementRef; + @ViewChild(ColorSelectorComponent, { static: false }) colorSelector: ColorSelectorComponent; @ViewChild('timeZoneInput') tzTypeAhead: NgbTypeahead; + tzFocus$ = new Subject(); tzClick$ = new Subject(); timeZones: string[] = []; originalTimeZone?: string; - @ViewChild('fileInput', { static: false }) fileInput: ElementRef; - @ViewChild(ColorSelectorComponent, { static: false }) colorSelector: ColorSelectorComponent; readonly ARTEMIS_DEFAULT_COLOR = ARTEMIS_DEFAULT_COLOR; + courseForm: FormGroup; course: Course; isSaving: boolean; @@ -79,7 +127,7 @@ export class CourseUpdateComponent implements OnInit { isAthenaEnabled = false; tutorialGroupsFeatureActivated = false; - private courseStorageServivce = inject(CourseStorageService); + private courseStorageService = inject(CourseStorageService); readonly semesters = getSemesters(); @@ -88,25 +136,8 @@ export class CourseUpdateComponent implements OnInit { // Currently set to 65535 as this is the limit of TEXT readonly COMPLAINT_RESPONSE_TEXT_LIMIT = 65535; readonly COMPLAINT_TEXT_LIMIT = 65535; - readonly COURSE_TITLE_LIMIT = 255; - constructor( - private eventManager: EventManager, - private courseManagementService: CourseManagementService, - private courseAdminService: CourseAdminService, - private activatedRoute: ActivatedRoute, - private fileService: FileService, - private alertService: AlertService, - private profileService: ProfileService, - private organizationService: OrganizationManagementService, - private modalService: NgbModal, - private navigationUtilService: ArtemisNavigationUtilService, - private router: Router, - private featureToggleService: FeatureToggleService, - private accountService: AccountService, - ) {} - ngOnInit() { this.timeZones = (Intl as any).supportedValuesOf('timeZone'); this.isSaving = false; @@ -347,7 +378,7 @@ export class CourseUpdateComponent implements OnInit { name: 'courseModification', content: 'Changed a course', }); - this.courseStorageServivce.updateCourse(updatedCourse!); + this.courseStorageService.updateCourse(updatedCourse!); } this.router.navigate(['course-management', updatedCourse?.id?.toString()]); @@ -367,13 +398,6 @@ export class CourseUpdateComponent implements OnInit { element.value = ''; } - /** - * @param event - */ - imageCropped(event: ImageCroppedEvent) { - this.croppedImage = event.base64; - } - /** * Action on unsuccessful course creation or edit * @param error The error for providing feedback diff --git a/src/main/webapp/app/course/manage/courses-for-dashboard-dto.ts b/src/main/webapp/app/course/manage/courses-for-dashboard-dto.ts index 123e334cca33..ad0b6d4a0a01 100644 --- a/src/main/webapp/app/course/manage/courses-for-dashboard-dto.ts +++ b/src/main/webapp/app/course/manage/courses-for-dashboard-dto.ts @@ -4,6 +4,4 @@ import { Exam } from 'app/entities/exam/exam.model'; export class CoursesForDashboardDTO { courses: CourseForDashboardDTO[]; activeExams: Exam[]; - - constructor() {} } diff --git a/src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts b/src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts index 6e842fdd4309..cc6d936bb531 100644 --- a/src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts +++ b/src/main/webapp/app/course/manage/detail/course-detail-doughnut-chart.component.ts @@ -1,12 +1,15 @@ -import { Component, Input, OnChanges, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnInit, inject } from '@angular/core'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; import { DoughnutChartType } from './course-detail.component'; -import { Router } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; import { Course } from 'app/entities/course.model'; -import { Color, ScaleType } from '@swimlane/ngx-charts'; +import { Color, PieChartModule, ScaleType } from '@swimlane/ngx-charts'; import { faSpinner } from '@fortawesome/free-solid-svg-icons'; import { NgxChartsSingleSeriesDataEntry } from 'app/shared/chart/ngx-charts-datatypes'; import { GraphColors } from 'app/entities/statistics.model'; +import { NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; const PIE_CHART_NA_FALLBACK_VALUE = [0, 0, 1]; @@ -14,8 +17,11 @@ const PIE_CHART_NA_FALLBACK_VALUE = [0, 0, 1]; selector: 'jhi-course-detail-doughnut-chart', templateUrl: './course-detail-doughnut-chart.component.html', styleUrls: ['./course-detail-doughnut-chart.component.scss'], + imports: [RouterLink, NgClass, FaIconComponent, PieChartModule, ArtemisTranslatePipe], }) export class CourseDetailDoughnutChartComponent implements OnChanges, OnInit { + private router = inject(Router); + @Input() contentType: DoughnutChartType; @Input() currentPercentage?: number; @Input() currentAbsolute?: number; @@ -31,8 +37,6 @@ export class CourseDetailDoughnutChartComponent implements OnChanges, OnInit { // Icons faSpinner = faSpinner; - constructor(private router: Router) {} - // ngx-charts ngxData: NgxChartsSingleSeriesDataEntry[] = [ { name: 'Done', value: 0 }, @@ -47,7 +51,7 @@ export class CourseDetailDoughnutChartComponent implements OnChanges, OnInit { } as Color; bindFormatting = this.valueFormatting.bind(this); - ngOnChanges(): void { + ngOnChanges() { // [0, 0, 0] will lead to the chart not being displayed, // assigning [0, 0, 1] (PIE_CHART_NA_FALLBACK_VALUE) works around this issue and displays 0 %, 0 / 0 with a grey circle if (this.currentAbsolute == undefined && !this.receivedStats && !this.showText) { diff --git a/src/main/webapp/app/course/manage/detail/course-detail-line-chart.component.ts b/src/main/webapp/app/course/manage/detail/course-detail-line-chart.component.ts index 60c30010f7de..17a273f95704 100644 --- a/src/main/webapp/app/course/manage/detail/course-detail-line-chart.component.ts +++ b/src/main/webapp/app/course/manage/detail/course-detail-line-chart.component.ts @@ -1,8 +1,8 @@ -import { Component, Input, OnChanges } from '@angular/core'; +import { Component, Input, OnChanges, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import dayjs from 'dayjs/esm'; import { CourseManagementService } from '../course-management.service'; -import { Color, ScaleType } from '@swimlane/ngx-charts'; +import { Color, LineChartModule, ScaleType } from '@swimlane/ngx-charts'; import { roundScorePercentSpecifiedByCourseSettings } from 'app/shared/util/utils'; import { Course } from 'app/entities/course.model'; import { faArrowLeft, faArrowRight, faSpinner } from '@fortawesome/free-solid-svg-icons'; @@ -10,6 +10,13 @@ import * as shape from 'd3-shape'; import { GraphColors } from 'app/entities/statistics.model'; import { ActiveStudentsChart } from 'app/shared/chart/active-students-chart'; import { mean } from 'simple-statistics'; +import { RouterLink } from '@angular/router'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export enum SwitchTimeSpanDirection { LEFT, @@ -20,8 +27,12 @@ export enum SwitchTimeSpanDirection { selector: 'jhi-course-detail-line-chart', templateUrl: './course-detail-line-chart.component.html', styleUrls: ['./course-detail-line-chart.component.scss'], + imports: [RouterLink, TranslateDirective, HelpIconComponent, NgbTooltip, FaIconComponent, LineChartModule, ArtemisDatePipe, ArtemisTranslatePipe], }) export class CourseDetailLineChartComponent extends ActiveStudentsChart implements OnChanges { + private service = inject(CourseManagementService); + private translateService = inject(TranslateService); + @Input() course: Course; @Input() @@ -70,10 +81,7 @@ export class CourseDetailLineChartComponent extends ActiveStudentsChart implemen faArrowLeft = faArrowLeft; faArrowRight = faArrowRight; - constructor( - private service: CourseManagementService, - private translateService: TranslateService, - ) { + constructor() { super(); this.translateService.onLangChange.subscribe(() => { this.loadTranslations(); diff --git a/src/main/webapp/app/course/manage/detail/course-detail.component.ts b/src/main/webapp/app/course/manage/detail/course-detail.component.ts index 4b5ecc80b629..b9e4057208cf 100644 --- a/src/main/webapp/app/course/manage/detail/course-detail.component.ts +++ b/src/main/webapp/app/course/manage/detail/course-detail.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { PROFILE_ATHENA, PROFILE_IRIS, PROFILE_LTI } from 'app/app.constants'; @@ -19,6 +19,9 @@ import { DetailOverviewSection, DetailType } from 'app/detail-overview-list/deta import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { IrisSubSettingsType } from 'app/entities/iris/settings/iris-sub-settings.model'; import { Detail } from 'app/detail-overview-list/detail.model'; +import { CourseDetailDoughnutChartComponent } from './course-detail-doughnut-chart.component'; +import { CourseDetailLineChartComponent } from './course-detail-line-chart.component'; +import { DetailOverviewListComponent } from 'app/detail-overview-list/detail-overview-list.component'; export enum DoughnutChartType { ASSESSMENT = 'ASSESSMENT', @@ -35,8 +38,20 @@ export enum DoughnutChartType { selector: 'jhi-course-detail', templateUrl: './course-detail.component.html', styleUrls: ['./course-detail.component.scss'], + imports: [CourseDetailDoughnutChartComponent, CourseDetailLineChartComponent, DetailOverviewListComponent], }) export class CourseDetailComponent implements OnInit, OnDestroy { + private eventManager = inject(EventManager); + private courseManagementService = inject(CourseManagementService); + private organizationService = inject(OrganizationManagementService); + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + private profileService = inject(ProfileService); + private accountService = inject(AccountService); + private irisSettingsService = inject(IrisSettingsService); + private markdownService = inject(ArtemisMarkdownService); + private featureToggleService = inject(FeatureToggleService); + readonly DoughnutChartType = DoughnutChartType; readonly FeatureToggle = FeatureToggle; @@ -69,19 +84,6 @@ export class CourseDetailComponent implements OnInit, OnDestroy { faChartBar = faChartBar; faClipboard = faClipboard; - constructor( - private eventManager: EventManager, - private courseManagementService: CourseManagementService, - private organizationService: OrganizationManagementService, - private route: ActivatedRoute, - private alertService: AlertService, - private profileService: ProfileService, - private accountService: AccountService, - private irisSettingsService: IrisSettingsService, - private markdownService: ArtemisMarkdownService, - private featureToggleService: FeatureToggleService, - ) {} - /** * On init load the course information and subscribe to listen for changes in courses. */ diff --git a/src/main/webapp/app/course/manage/image-cropper-modal.component.ts b/src/main/webapp/app/course/manage/image-cropper-modal.component.ts index 12944633f6e0..d327a064c8bb 100644 --- a/src/main/webapp/app/course/manage/image-cropper-modal.component.ts +++ b/src/main/webapp/app/course/manage/image-cropper-modal.component.ts @@ -1,20 +1,23 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ImageCroppedEvent } from 'app/shared/image-cropper/interfaces/image-cropped-event.interface'; import { OutputFormat } from 'app/shared/image-cropper/interfaces/cropper-options.interface'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ImageCropperComponent } from 'app/shared/image-cropper/component/image-cropper.component'; @Component({ selector: 'jhi-image-cropper-modal', templateUrl: './image-cropper-modal.component.html', + imports: [TranslateDirective, ImageCropperComponent], }) export class ImageCropperModalComponent { + private activeModal = inject(NgbActiveModal); + uploadFile?: File; croppedImage?: string; roundCropper = true; fileFormat: OutputFormat = 'png'; - constructor(private activeModal: NgbActiveModal) {} - /** * Called when an image is cropped. * @param event The event containing the cropped image data. diff --git a/src/main/webapp/app/course/manage/overview/course-management-card.component.ts b/src/main/webapp/app/course/manage/overview/course-management-card.component.ts index 2fd25af861ed..436def467956 100644 --- a/src/main/webapp/app/course/manage/overview/course-management-card.component.ts +++ b/src/main/webapp/app/course/manage/overview/course-management-card.component.ts @@ -25,12 +25,38 @@ import { faUserCheck, } from '@fortawesome/free-solid-svg-icons'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; +import { NgStyle } from '@angular/common'; +import { RouterLink } from '@angular/router'; +import { SecuredImageComponent } from 'app/shared/image/secured-image.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { CourseManagementExerciseRowComponent } from './course-management-exercise-row.component'; +import { CourseManagementOverviewStatisticsComponent } from './course-management-overview-statistics.component'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FeatureToggleLinkDirective } from 'app/shared/feature-toggle/feature-toggle-link.directive'; +import { FeatureToggleHideDirective } from 'app/shared/feature-toggle/feature-toggle-hide.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-course-management-card', templateUrl: './course-management-card.component.html', styleUrls: ['course-management-card.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgStyle, + RouterLink, + SecuredImageComponent, + TranslateDirective, + FaIconComponent, + CourseManagementExerciseRowComponent, + CourseManagementOverviewStatisticsComponent, + NgbTooltip, + FeatureToggleLinkDirective, + FeatureToggleHideDirective, + ArtemisDatePipe, + ArtemisTranslatePipe, + ], }) export class CourseManagementCardComponent implements OnChanges { readonly ARTEMIS_DEFAULT_COLOR = ARTEMIS_DEFAULT_COLOR; diff --git a/src/main/webapp/app/course/manage/overview/course-management-exercise-row.component.ts b/src/main/webapp/app/course/manage/overview/course-management-exercise-row.component.ts index 1a2b62285a3a..c69035905d6a 100644 --- a/src/main/webapp/app/course/manage/overview/course-management-exercise-row.component.ts +++ b/src/main/webapp/app/course/manage/overview/course-management-exercise-row.component.ts @@ -6,6 +6,16 @@ import { Course } from 'app/entities/course.model'; import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils'; import { faBook, faExclamationTriangle, faFileSignature, faTable, faTimes, faUsers, faWrench } from '@fortawesome/free-solid-svg-icons'; import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ExerciseCategoriesComponent } from 'app/shared/exercise-categories/exercise-categories.component'; +import { NgClass } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ProgressBarComponent } from 'app/shared/dashboards/tutor-participation-graph/progress-bar/progress-bar.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisTimeAgoPipe } from 'app/shared/pipes/artemis-time-ago.pipe'; export enum ExerciseRowType { FUTURE = 'future', @@ -19,6 +29,18 @@ export enum ExerciseRowType { templateUrl: './course-management-exercise-row.component.html', styleUrls: ['course-management-exercise-row.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + RouterLink, + FaIconComponent, + NgbTooltip, + ExerciseCategoriesComponent, + NgClass, + TranslateDirective, + ProgressBarComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + ArtemisTimeAgoPipe, + ], }) export class CourseManagementExerciseRowComponent implements OnChanges { @Input() course: Course; @@ -57,8 +79,6 @@ export class CourseManagementExerciseRowComponent implements OnChanges { faExclamationTriangle = faExclamationTriangle; faFileSignature = faFileSignature; - constructor() {} - ngOnChanges() { if (this.details && !this.detailsLoaded) { this.detailsLoaded = true; diff --git a/src/main/webapp/app/course/manage/overview/course-management-overview-exercise-statistics-dto.model.ts b/src/main/webapp/app/course/manage/overview/course-management-overview-exercise-statistics-dto.model.ts index 7780cdcdd19c..3738b17e0e01 100644 --- a/src/main/webapp/app/course/manage/overview/course-management-overview-exercise-statistics-dto.model.ts +++ b/src/main/webapp/app/course/manage/overview/course-management-overview-exercise-statistics-dto.model.ts @@ -9,6 +9,4 @@ export class CourseManagementOverviewExerciseStatisticsDTO { public noOfRatedAssessments?: number; public noOfSubmissionsInTime?: number; public noOfAssessmentsDoneInPercent?: number; - - constructor() {} } diff --git a/src/main/webapp/app/course/manage/overview/course-management-overview-statistics-dto.model.ts b/src/main/webapp/app/course/manage/overview/course-management-overview-statistics-dto.model.ts index 4195209c07e7..7540cf386005 100644 --- a/src/main/webapp/app/course/manage/overview/course-management-overview-statistics-dto.model.ts +++ b/src/main/webapp/app/course/manage/overview/course-management-overview-statistics-dto.model.ts @@ -4,6 +4,4 @@ export class CourseManagementOverviewStatisticsDto { public courseId?: number; public exerciseDTOS: CourseManagementOverviewExerciseStatisticsDTO[]; public activeStudents?: number[]; - - constructor() {} } diff --git a/src/main/webapp/app/course/manage/overview/course-management-overview-statistics.component.ts b/src/main/webapp/app/course/manage/overview/course-management-overview-statistics.component.ts index 1d155291a83b..8fbb9f5b03e4 100644 --- a/src/main/webapp/app/course/manage/overview/course-management-overview-statistics.component.ts +++ b/src/main/webapp/app/course/manage/overview/course-management-overview-statistics.component.ts @@ -1,18 +1,25 @@ -import { Component, Input, OnChanges, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnInit, inject } from '@angular/core'; import { GraphColors, Graphs } from 'app/entities/statistics.model'; import { TranslateService } from '@ngx-translate/core'; -import { Color, ScaleType } from '@swimlane/ngx-charts'; +import { Color, LineChartModule, ScaleType } from '@swimlane/ngx-charts'; import { faSpinner } from '@fortawesome/free-solid-svg-icons'; import { Course } from 'app/entities/course.model'; import * as shape from 'd3-shape'; import { ActiveStudentsChart } from 'app/shared/chart/active-students-chart'; +import { RouterLink } from '@angular/router'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-course-management-overview-statistics', templateUrl: './course-management-overview-statistics.component.html', styleUrls: ['./course-management-overview-statistics.component.scss', '../detail/course-detail-line-chart.component.scss'], + imports: [RouterLink, TranslateDirective, HelpIconComponent, LineChartModule, ArtemisDatePipe], }) export class CourseManagementOverviewStatisticsComponent extends ActiveStudentsChart implements OnInit, OnChanges { + private translateService = inject(TranslateService); + @Input() amountOfStudentsInCourse: number; @@ -39,7 +46,7 @@ export class CourseManagementOverviewStatisticsComponent extends ActiveStudentsC // Icons faSpinner = faSpinner; - constructor(private translateService: TranslateService) { + constructor() { super(); } diff --git a/src/main/webapp/app/course/plagiarism-cases/instructor-view/detail-view/plagiarism-case-instructor-detail-view.component.ts b/src/main/webapp/app/course/plagiarism-cases/instructor-view/detail-view/plagiarism-case-instructor-detail-view.component.ts index 1b2265a266aa..3249ffdc298c 100644 --- a/src/main/webapp/app/course/plagiarism-cases/instructor-view/detail-view/plagiarism-case-instructor-detail-view.component.ts +++ b/src/main/webapp/app/course/plagiarism-cases/instructor-view/detail-view/plagiarism-case-instructor-detail-view.component.ts @@ -1,7 +1,9 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { PlagiarismCaseReviewComponent } from 'app/course/plagiarism-cases/shared/review/plagiarism-case-review.component'; +import { PlagiarismCaseVerdictComponent } from 'app/course/plagiarism-cases/shared/verdict/plagiarism-case-verdict.component'; import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase'; import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagiarism-cases.service'; -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { HttpResponse } from '@angular/common/http'; import { getCourseFromExercise, getExerciseUrlSegment, getIcon } from 'app/entities/exercise.model'; @@ -17,14 +19,63 @@ import { abbreviateString } from 'app/utils/text.utils'; import { AccountService } from 'app/core/auth/account.service'; import { User } from 'app/core/user/user.model'; import dayjs from 'dayjs/esm'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { + NgbDropdown, + NgbDropdownItem, + NgbDropdownMenu, + NgbDropdownToggle, + NgbNav, + NgbNavContent, + NgbNavItem, + NgbNavItemRole, + NgbNavLink, + NgbNavLinkBase, + NgbNavOutlet, +} from '@ng-bootstrap/ng-bootstrap'; +import { PostingThreadComponent } from 'app/shared/metis/posting-thread/posting-thread.component'; +import { PostCreateEditModalComponent } from 'app/shared/metis/posting-create-edit-modal/post-create-edit-modal/post-create-edit-modal.component'; +import { ConfirmAutofocusButtonComponent } from 'app/shared/components/confirm-autofocus-button.component'; +import { FormsModule } from '@angular/forms'; @Component({ selector: 'jhi-plagiarism-case-instructor-detail-view', templateUrl: './plagiarism-case-instructor-detail-view.component.html', styleUrls: ['./plagiarism-case-instructor-detail-view.component.scss'], providers: [MetisService], + imports: [ + TranslateDirective, + PlagiarismCaseVerdictComponent, + FaIconComponent, + RouterLink, + NgbDropdown, + NgbDropdownToggle, + NgbDropdownMenu, + NgbDropdownItem, + PostingThreadComponent, + PostCreateEditModalComponent, + NgbNav, + NgbNavItem, + NgbNavItemRole, + NgbNavLink, + NgbNavLinkBase, + NgbNavContent, + ConfirmAutofocusButtonComponent, + FormsModule, + NgbNavOutlet, + PlagiarismCaseReviewComponent, + ], }) export class PlagiarismCaseInstructorDetailViewComponent implements OnInit, OnDestroy { + protected metisService = inject(MetisService); + private plagiarismCasesService = inject(PlagiarismCasesService); + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + private translateService = inject(TranslateService); + private themeService = inject(ThemeService); + private accountService = inject(AccountService); + courseId: number; plagiarismCaseId: number; plagiarismCase: PlagiarismCase; @@ -47,16 +98,6 @@ export class PlagiarismCaseInstructorDetailViewComponent implements OnInit, OnDe private postsSubscription: Subscription; posts: Post[]; - constructor( - protected metisService: MetisService, - private plagiarismCasesService: PlagiarismCasesService, - private route: ActivatedRoute, - private alertService: AlertService, - private translateService: TranslateService, - private themeService: ThemeService, - private accountService: AccountService, - ) {} - ngOnInit(): void { this.courseId = Number(this.route.snapshot.paramMap.get('courseId')); this.plagiarismCaseId = Number(this.route.snapshot.paramMap.get('plagiarismCaseId')); diff --git a/src/main/webapp/app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.component.ts b/src/main/webapp/app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.component.ts index efa0d14d147e..3a4137a35740 100644 --- a/src/main/webapp/app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.component.ts +++ b/src/main/webapp/app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.component.ts @@ -1,6 +1,6 @@ import { HttpResponse } from '@angular/common/http'; -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagiarism-cases.service'; import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase'; import { Exercise, getExerciseUrlSegment, getIcon } from 'app/entities/exercise.model'; @@ -8,13 +8,34 @@ import { downloadFile } from 'app/shared/util/download.util'; import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component'; import { GroupedPlagiarismCases } from 'app/exercises/shared/plagiarism/types/GroupedPlagiarismCase'; import { AlertService } from 'app/core/util/alert.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ProgressBarComponent } from 'app/shared/dashboards/tutor-participation-graph/progress-bar/progress-bar.component'; +import { PlagiarismCaseVerdictComponent } from '../shared/verdict/plagiarism-case-verdict.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-plagiarism-cases-instructor-view', templateUrl: './plagiarism-cases-instructor-view.component.html', styleUrls: ['./plagiarism-cases-instructor-view.component.scss'], + imports: [ + TranslateDirective, + DocumentationButtonComponent, + FaIconComponent, + RouterLink, + ProgressBarComponent, + PlagiarismCaseVerdictComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + ], }) export class PlagiarismCasesInstructorViewComponent implements OnInit { + private plagiarismCasesService = inject(PlagiarismCasesService); + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + courseId: number; examId?: number; plagiarismCases: PlagiarismCase[] = []; @@ -28,12 +49,6 @@ export class PlagiarismCasesInstructorViewComponent implements OnInit { readonly getIcon = getIcon; readonly documentationType: DocumentationType = 'PlagiarismChecks'; - constructor( - private plagiarismCasesService: PlagiarismCasesService, - private route: ActivatedRoute, - private alertService: AlertService, - ) {} - ngOnInit(): void { this.courseId = Number(this.route.snapshot.paramMap.get('courseId')); this.examId = Number(this.route.snapshot.paramMap.get('examId')); diff --git a/src/main/webapp/app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.module.ts b/src/main/webapp/app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.module.ts index 4c4241f2da8d..014af93d4b6b 100644 --- a/src/main/webapp/app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.module.ts +++ b/src/main/webapp/app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.module.ts @@ -1,7 +1,8 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; -import { PlagiarismCasesInstructorViewComponent } from 'app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.component'; import { PlagiarismCaseInstructorDetailViewComponent } from 'app/course/plagiarism-cases/instructor-view/detail-view/plagiarism-case-instructor-detail-view.component'; +import { PlagiarismCasesInstructorViewComponent } from 'app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.component'; + import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { ArtemisPlagiarismCasesSharedModule } from 'app/course/plagiarism-cases/shared/plagiarism-cases-shared.module'; @@ -9,7 +10,7 @@ import { ArtemisPlagiarismCasesSharedModule } from 'app/course/plagiarism-cases/ const routes: Routes = [ { path: '', - component: PlagiarismCasesInstructorViewComponent, + loadComponent: () => import('app/course/plagiarism-cases/instructor-view/plagiarism-cases-instructor-view.component').then((m) => m.PlagiarismCasesInstructorViewComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.plagiarism.cases.pageTitle', @@ -18,7 +19,10 @@ const routes: Routes = [ }, { path: ':plagiarismCaseId', - component: PlagiarismCaseInstructorDetailViewComponent, + loadComponent: () => + import('app/course/plagiarism-cases/instructor-view/detail-view/plagiarism-case-instructor-detail-view.component').then( + (m) => m.PlagiarismCaseInstructorDetailViewComponent, + ), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.plagiarism.cases.pageTitle', @@ -28,7 +32,6 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forChild(routes), ArtemisPlagiarismCasesSharedModule], - declarations: [PlagiarismCasesInstructorViewComponent, PlagiarismCaseInstructorDetailViewComponent], + imports: [RouterModule.forChild(routes), ArtemisPlagiarismCasesSharedModule, PlagiarismCasesInstructorViewComponent, PlagiarismCaseInstructorDetailViewComponent], }) export class ArtemisPlagiarismCasesInstructorViewModule {} diff --git a/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-cases-shared.module.ts b/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-cases-shared.module.ts index f21a9fc2cd06..c0397b829ac0 100644 --- a/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-cases-shared.module.ts +++ b/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-cases-shared.module.ts @@ -8,8 +8,15 @@ import { PlagiarismCaseReviewComponent } from 'app/course/plagiarism-cases/share import { MetisModule } from 'app/shared/metis/metis.module'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisSharedComponentModule, ArtemisPlagiarismModule, ArtemisTutorParticipationGraphModule, MetisModule], - declarations: [PlagiarismCaseVerdictComponent, PlagiarismCaseReviewComponent], + imports: [ + ArtemisSharedModule, + ArtemisSharedComponentModule, + ArtemisPlagiarismModule, + ArtemisTutorParticipationGraphModule, + MetisModule, + PlagiarismCaseVerdictComponent, + PlagiarismCaseReviewComponent, + ], exports: [ PlagiarismCaseVerdictComponent, PlagiarismCaseReviewComponent, diff --git a/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-cases.service.ts b/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-cases.service.ts index e4041a996cf4..c3fea5b08795 100644 --- a/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-cases.service.ts +++ b/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-cases.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase'; @@ -15,11 +15,11 @@ export type Comparison = PlagiarismComparison; @Injectable({ providedIn: 'root' }) export class PlagiarismCasesService { + private http = inject(HttpClient); + private resourceUrl = 'api/courses'; private resourceUrlExercises = 'api/exercises'; - constructor(private http: HttpClient) {} - /* Instructor */ /** diff --git a/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-results.service.ts b/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-results.service.ts index 28652db616eb..4626a00ad485 100644 --- a/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-results.service.ts +++ b/src/main/webapp/app/course/plagiarism-cases/shared/plagiarism-results.service.ts @@ -1,12 +1,12 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class PlagiarismResultsService { - private resourceUrlExercises = 'api/exercises'; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + private resourceUrlExercises = 'api/exercises'; getNumberOfPlagiarismResultsForExercise(exerciseId: number): Observable { return this.http.get(`${this.resourceUrlExercises}/${exerciseId}/potential-plagiarism-count`); diff --git a/src/main/webapp/app/course/plagiarism-cases/shared/review/plagiarism-case-review.component.ts b/src/main/webapp/app/course/plagiarism-cases/shared/review/plagiarism-case-review.component.ts index a8ed39faa5d0..67037909c24c 100644 --- a/src/main/webapp/app/course/plagiarism-cases/shared/review/plagiarism-case-review.component.ts +++ b/src/main/webapp/app/course/plagiarism-cases/shared/review/plagiarism-case-review.component.ts @@ -1,10 +1,14 @@ import { Component, Input } from '@angular/core'; import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase'; import { Subject } from 'rxjs'; +import { NgbNav, NgbNavContent, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavLinkBase, NgbNavOutlet } from '@ng-bootstrap/ng-bootstrap'; +import { PlagiarismSplitViewComponent } from 'app/exercises/shared/plagiarism/plagiarism-split-view/plagiarism-split-view.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-plagiarism-case-review', templateUrl: './plagiarism-case-review.component.html', + imports: [NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavLinkBase, NgbNavContent, PlagiarismSplitViewComponent, NgbNavOutlet, ArtemisTranslatePipe], }) export class PlagiarismCaseReviewComponent { @Input() plagiarismCase: PlagiarismCase; diff --git a/src/main/webapp/app/course/plagiarism-cases/shared/verdict/plagiarism-case-verdict.component.ts b/src/main/webapp/app/course/plagiarism-cases/shared/verdict/plagiarism-case-verdict.component.ts index f1b404044906..d2d0d1a5c2d6 100644 --- a/src/main/webapp/app/course/plagiarism-cases/shared/verdict/plagiarism-case-verdict.component.ts +++ b/src/main/webapp/app/course/plagiarism-cases/shared/verdict/plagiarism-case-verdict.component.ts @@ -1,10 +1,14 @@ import { Component, Input } from '@angular/core'; import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase'; import { PlagiarismVerdict } from 'app/exercises/shared/plagiarism/types/PlagiarismVerdict'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-plagiarism-case-verdict', templateUrl: './plagiarism-case-verdict.component.html', + imports: [NgbTooltip, ArtemisDatePipe, ArtemisTranslatePipe], }) export class PlagiarismCaseVerdictComponent { @Input() plagiarismCase: PlagiarismCase; diff --git a/src/main/webapp/app/course/plagiarism-cases/student-view/detail-view/plagiarism-case-student-detail-view.component.ts b/src/main/webapp/app/course/plagiarism-cases/student-view/detail-view/plagiarism-case-student-detail-view.component.ts index feb0bf3c8d02..30fe9f16082c 100644 --- a/src/main/webapp/app/course/plagiarism-cases/student-view/detail-view/plagiarism-case-student-detail-view.component.ts +++ b/src/main/webapp/app/course/plagiarism-cases/student-view/detail-view/plagiarism-case-student-detail-view.component.ts @@ -1,7 +1,9 @@ -import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; +import { PlagiarismCaseReviewComponent } from 'app/course/plagiarism-cases/shared/review/plagiarism-case-review.component'; +import { PlagiarismCaseVerdictComponent } from 'app/course/plagiarism-cases/shared/verdict/plagiarism-case-verdict.component'; import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase'; import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagiarism-cases.service'; -import { ActivatedRoute, Params } from '@angular/router'; +import { ActivatedRoute, Params, RouterLink } from '@angular/router'; import { HttpResponse } from '@angular/common/http'; import { getCourseFromExercise, getIcon } from 'app/entities/exercise.model'; import { Subscription, combineLatest } from 'rxjs'; @@ -13,14 +15,23 @@ import { PlagiarismVerdict } from 'app/exercises/shared/plagiarism/types/Plagiar import { PostComponent } from 'app/shared/metis/post/post.component'; import { ButtonType } from 'app/shared/components/button.component'; import dayjs from 'dayjs/esm'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-plagiarism-case-student-detail-view', templateUrl: './plagiarism-case-student-detail-view.component.html', styleUrls: ['./plagiarism-case-student-detail-view.component.scss'], providers: [MetisService], + imports: [TranslateDirective, PlagiarismCaseVerdictComponent, FaIconComponent, RouterLink, PostComponent, ButtonComponent, PlagiarismCaseReviewComponent, ArtemisTranslatePipe], }) export class PlagiarismCaseStudentDetailViewComponent implements OnInit, OnDestroy { + protected metisService = inject(MetisService); + private plagiarismCasesService = inject(PlagiarismCasesService); + private activatedRoute = inject(ActivatedRoute); + @ViewChild('post') postComponent: PostComponent; readonly ButtonType = ButtonType; @@ -44,12 +55,6 @@ export class PlagiarismCaseStudentDetailViewComponent implements OnInit, OnDestr readonly dayjs = dayjs; - constructor( - protected metisService: MetisService, - private plagiarismCasesService: PlagiarismCasesService, - private activatedRoute: ActivatedRoute, - ) {} - ngOnInit(): void { this.paramSubscription = combineLatest({ ancestorParams: this.activatedRoute.parent!.parent!.params, diff --git a/src/main/webapp/app/course/plagiarism-cases/student-view/plagiarism-cases-student-view.module.ts b/src/main/webapp/app/course/plagiarism-cases/student-view/plagiarism-cases-student-view.module.ts index 373e04e0616a..081f4156c14e 100644 --- a/src/main/webapp/app/course/plagiarism-cases/student-view/plagiarism-cases-student-view.module.ts +++ b/src/main/webapp/app/course/plagiarism-cases/student-view/plagiarism-cases-student-view.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { PlagiarismCaseStudentDetailViewComponent } from 'app/course/plagiarism-cases/student-view/detail-view/plagiarism-case-student-detail-view.component'; + import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; import { ArtemisPlagiarismCasesSharedModule } from 'app/course/plagiarism-cases/shared/plagiarism-cases-shared.module'; @@ -8,7 +9,8 @@ import { ArtemisPlagiarismCasesSharedModule } from 'app/course/plagiarism-cases/ const routes: Routes = [ { path: ':plagiarismCaseId', - component: PlagiarismCaseStudentDetailViewComponent, + loadComponent: () => + import('app/course/plagiarism-cases/student-view/detail-view/plagiarism-case-student-detail-view.component').then((m) => m.PlagiarismCaseStudentDetailViewComponent), data: { authorities: [Authority.USER, Authority.ADMIN], pageTitle: 'artemisApp.plagiarism.cases.pageTitle', @@ -18,7 +20,6 @@ const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forChild(routes), ArtemisPlagiarismCasesSharedModule], - declarations: [PlagiarismCaseStudentDetailViewComponent], + imports: [RouterModule.forChild(routes), ArtemisPlagiarismCasesSharedModule, PlagiarismCaseStudentDetailViewComponent], }) export class ArtemisPlagiarismCasesStudentViewModule {} diff --git a/src/main/webapp/app/course/tutorial-groups/shared/meeting-pattern.pipe.ts b/src/main/webapp/app/course/tutorial-groups/shared/meeting-pattern.pipe.ts index 2cd9b89c262c..cff6004c3e11 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/meeting-pattern.pipe.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/meeting-pattern.pipe.ts @@ -9,13 +9,12 @@ import { RemoveSecondsPipe } from 'app/course/tutorial-groups/shared/remove-seco * Example: 'Every Week, Monday from 14:00 to 15:00' English * Example: 'Jede Woche, Montag von 14:00 bis 15:00' German */ -@Pipe({ - name: 'meetingPattern', -}) +@Pipe({ name: 'meetingPattern' }) export class MeetingPatternPipe implements PipeTransform { removeSecondsPipe = new RemoveSecondsPipe(); constructor(private translateService: TranslateService) {} + /** * Transforms a tutorial group schedule to a translated meeting pattern. * @param schedule The tutorial group schedule to transform. diff --git a/src/main/webapp/app/course/tutorial-groups/shared/remove-seconds.pipe.ts b/src/main/webapp/app/course/tutorial-groups/shared/remove-seconds.pipe.ts index af351b9d516c..5817338b88e0 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/remove-seconds.pipe.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/remove-seconds.pipe.ts @@ -3,9 +3,7 @@ import { Pipe, PipeTransform } from '@angular/core'; /** * A pipe that removes the seconds information from a time string in the format '14:00:00'. */ -@Pipe({ - name: 'removeSeconds', -}) +@Pipe({ name: 'removeSeconds' }) export class RemoveSecondsPipe implements PipeTransform { /** * Transforms a time string in the format '14:00:00' to a string without seconds, e.g. '14:00'. diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts index 2b3adc91f4e3..97f692fb8d54 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, ContentChild, Input, OnChanges, SimpleChanges, TemplateRef } from '@angular/core'; +import { ChangeDetectorRef, Component, ContentChild, Input, OnChanges, SimpleChanges, TemplateRef, inject } from '@angular/core'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { Course, isMessagingEnabled } from 'app/entities/course.model'; import { SafeHtml } from '@angular/platform-browser'; @@ -9,13 +9,40 @@ import { TranslateService } from '@ngx-translate/core'; import { faCircle, faCircleInfo, faCircleXmark, faPercent, faQuestionCircle, faUserCheck } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { SortService } from 'app/shared/service/sort.service'; +import { NgClass, NgStyle, NgTemplateOutlet } from '@angular/common'; +import { IconCardComponent } from 'app/shared/icon-card/icon-card.component'; +import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { TutorialGroupSessionsTableComponent } from '../tutorial-group-sessions-table/tutorial-group-sessions-table.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutorial-group-detail', templateUrl: './tutorial-group-detail.component.html', styleUrls: ['./tutorial-group-detail.component.scss'], + imports: [ + NgTemplateOutlet, + IconCardComponent, + ProfilePictureComponent, + TranslateDirective, + RouterLink, + FaIconComponent, + NgbTooltip, + NgClass, + NgStyle, + TutorialGroupSessionsTableComponent, + ArtemisTranslatePipe, + ], }) export class TutorialGroupDetailComponent implements OnChanges { + private artemisMarkdownService = inject(ArtemisMarkdownService); + private changeDetectorRef = inject(ChangeDetectorRef); + private translateService = inject(TranslateService); + private sortService = inject(SortService); + @ContentChild(TemplateRef, { static: true }) header: TemplateRef; @Input() @@ -44,13 +71,6 @@ export class TutorialGroupDetailComponent implements OnChanges { readonly faCircle = faCircle; readonly faCircleXmark = faCircleXmark; - constructor( - private artemisMarkdownService: ArtemisMarkdownService, - private changeDetectorRef: ChangeDetectorRef, - private translateService: TranslateService, - private sortService: SortService, - ) {} - ngOnChanges(changes: SimpleChanges) { for (const propName in changes) { if (changes.hasOwnProperty(propName) && propName === 'tutorialGroup') { diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-free-days-overview/tutorial-group-free-days-overview.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-free-days-overview/tutorial-group-free-days-overview.component.ts index 97226684b4a0..b23b5b89e17a 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-free-days-overview/tutorial-group-free-days-overview.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-free-days-overview/tutorial-group-free-days-overview.component.ts @@ -1,19 +1,24 @@ -import { ChangeDetectionStrategy, Component, DoCheck, Input, IterableDiffer, IterableDiffers, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, DoCheck, Input, IterableDiffer, IterableDiffers, OnInit, inject } from '@angular/core'; import { TutorialGroupFreePeriod } from 'app/entities/tutorial-group/tutorial-group-free-day.model'; import { SortService } from 'app/shared/service/sort.service'; import dayjs from 'dayjs/esm'; +import { SidePanelComponent } from 'app/shared/side-panel/side-panel.component'; +import { NgClass } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutorial-group-free-days-overview', templateUrl: './tutorial-group-free-days-overview.component.html', styleUrls: ['./tutorial-group-free-days-overview.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [SidePanelComponent, NgClass, TranslateDirective, NgbPopover, ArtemisDatePipe, ArtemisTranslatePipe], }) export class TutorialGroupFreeDaysOverviewComponent implements OnInit, DoCheck { - constructor( - private sortService: SortService, - private iterableDiffers: IterableDiffers, - ) {} + private sortService = inject(SortService); + private iterableDiffers = inject(IterableDiffers); @Input() tutorialGroupFreeDays: TutorialGroupFreePeriod[] = []; diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-sessions-table/tutorial-group-session-row/tutorial-group-session-row.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-sessions-table/tutorial-group-session-row/tutorial-group-session-row.component.ts index 02fe89acac8b..bb28f1a9e113 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-sessions-table/tutorial-group-session-row/tutorial-group-session-row.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-sessions-table/tutorial-group-session-row/tutorial-group-session-row.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostBinding, Input, OnChanges, Output, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostBinding, Input, OnChanges, Output, TemplateRef, ViewEncapsulation, inject } from '@angular/core'; import { faUmbrellaBeach } from '@fortawesome/free-solid-svg-icons'; import { TutorialGroupSession, TutorialGroupSessionStatus } from 'app/entities/tutorial-group/tutorial-group-session.model'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; @@ -7,17 +7,28 @@ import { onError } from 'app/shared/util/global.utils'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; import { map } from 'rxjs'; +import { NgbPopover } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgTemplateOutlet } from '@angular/common'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ // this is intended and an attribute selector because otherwise the rendered table breaks - // eslint-disable-next-line @angular-eslint/component-selector selector: '[jhi-session-row]', templateUrl: './tutorial-group-session-row.component.html', styleUrls: ['./tutorial-group-session-row.component.scss'], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, + imports: [NgbPopover, FaIconComponent, FormsModule, TranslateDirective, NgTemplateOutlet, ArtemisDatePipe, ArtemisTranslatePipe], }) export class TutorialGroupSessionRowComponent implements OnChanges { + private changeDetectorRef = inject(ChangeDetectorRef); + private tutorialGroupSessionService = inject(TutorialGroupSessionService); + private alertService = inject(AlertService); + @HostBinding('class') class = 'tutorial-group-session-row'; @Input() @@ -48,12 +59,6 @@ export class TutorialGroupSessionRowComponent implements OnChanges { hasSchedule = false; - constructor( - private changeDetectorRef: ChangeDetectorRef, - private tutorialGroupSessionService: TutorialGroupSessionService, - private alertService: AlertService, - ) {} - ngOnChanges() { if (this.session) { this.isCancelled = this.session.status === TutorialGroupSessionStatus.CANCELLED; diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-sessions-table/tutorial-group-sessions-table.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-sessions-table/tutorial-group-sessions-table.component.ts index f21326185326..f94d5feb4a88 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-sessions-table/tutorial-group-sessions-table.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-sessions-table/tutorial-group-sessions-table.component.ts @@ -10,11 +10,16 @@ import { SimpleChanges, TemplateRef, ViewEncapsulation, + inject, } from '@angular/core'; import { TutorialGroupSession } from 'app/entities/tutorial-group/tutorial-group-session.model'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { SortService } from 'app/shared/service/sort.service'; import dayjs from 'dayjs/esm'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TutorialGroupSessionRowComponent } from './tutorial-group-session-row/tutorial-group-session-row.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutorial-group-sessions-table', @@ -22,8 +27,12 @@ import dayjs from 'dayjs/esm'; styleUrls: ['./tutorial-group-sessions-table.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, + imports: [TranslateDirective, TutorialGroupSessionRowComponent, ArtemisDatePipe, ArtemisTranslatePipe], }) export class TutorialGroupSessionsTableComponent implements OnChanges { + private sortService = inject(SortService); + private changeDetectorRef = inject(ChangeDetectorRef); + @ContentChild(TemplateRef, { static: true }) extraColumn: TemplateRef; @Input() @@ -62,11 +71,6 @@ export class TutorialGroupSessionsTableComponent implements OnChanges { return numberOfColumns; } - constructor( - private sortService: SortService, - private changeDetectorRef: ChangeDetectorRef, - ) {} - ngOnChanges(changes: SimpleChanges) { for (const propName in changes) { if (changes.hasOwnProperty(propName)) { diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-utilization-indicator/tutorial-group-utilization-indicator.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-utilization-indicator/tutorial-group-utilization-indicator.component.ts index e6a34005ba84..00507fe08da5 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-utilization-indicator/tutorial-group-utilization-indicator.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-utilization-indicator/tutorial-group-utilization-indicator.component.ts @@ -1,11 +1,14 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; +import { VerticalProgressBarComponent } from 'app/shared/vertical-progress-bar/vertical-progress-bar.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutorial-group-utilization-indicator', templateUrl: './tutorial-group-utilization-indicator.component.html', styleUrls: ['./tutorial-group-utilization-indicator.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [VerticalProgressBarComponent, ArtemisTranslatePipe], }) export class TutorialGroupUtilizationIndicatorComponent { readonly Math = Math; diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-shared.module.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-shared.module.ts index 3c1417c6a2b6..47820f440d42 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-shared.module.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-shared.module.ts @@ -8,7 +8,7 @@ import { TutorialGroupSessionRowComponent } from 'app/course/tutorial-groups/sha import { TutorialGroupFreeDaysOverviewComponent } from './tutorial-group-free-days-overview/tutorial-group-free-days-overview.component'; import { ArtemisSidePanelModule } from 'app/shared/side-panel/side-panel.module'; import { TutorialGroupRowComponent } from './tutorial-groups-table/tutorial-group-row/tutorial-group-row.component'; -import { VerticalProgressBarModule } from 'app/shared/vertical-progress-bar/vertical-progress-bar.module'; + import { TutorialGroupUtilizationIndicatorComponent } from './tutorial-group-utilization-indicator/tutorial-group-utilization-indicator.component'; import { RemoveSecondsPipe } from 'app/course/tutorial-groups/shared/remove-seconds.pipe'; import { MeetingPatternPipe } from 'app/course/tutorial-groups/shared/meeting-pattern.pipe'; @@ -17,8 +17,13 @@ import { IconCardComponent } from 'app/shared/icon-card/icon-card.component'; import { ProfilePictureComponent } from 'app/shared/profile-picture/profile-picture.component'; @NgModule({ - imports: [ArtemisSharedModule, RouterModule, ArtemisSidePanelModule, VerticalProgressBarModule, DetailModule, IconCardComponent, ProfilePictureComponent], - declarations: [ + imports: [ + ArtemisSharedModule, + RouterModule, + ArtemisSidePanelModule, + DetailModule, + IconCardComponent, + ProfilePictureComponent, TutorialGroupsTableComponent, TutorialGroupDetailComponent, TutorialGroupSessionsTableComponent, diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-group-row/tutorial-group-row.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-group-row/tutorial-group-row.component.ts index 4c8d5dd58a4d..15c240ac3263 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-group-row/tutorial-group-row.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-group-row/tutorial-group-row.component.ts @@ -1,14 +1,21 @@ import { ChangeDetectionStrategy, Component, HostBinding, Input, TemplateRef, ViewEncapsulation } from '@angular/core'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { Course } from 'app/entities/course.model'; +import { RouterLink } from '@angular/router'; +import { TutorialGroupUtilizationIndicatorComponent } from '../../tutorial-group-utilization-indicator/tutorial-group-utilization-indicator.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgTemplateOutlet } from '@angular/common'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { MeetingPatternPipe } from 'app/course/tutorial-groups/shared/meeting-pattern.pipe'; @Component({ - // eslint-disable-next-line @angular-eslint/component-selector selector: '[jhi-tutorial-group-row]', templateUrl: './tutorial-group-row.component.html', styleUrls: ['./tutorial-group-row.component.scss'], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, + imports: [RouterLink, TutorialGroupUtilizationIndicatorComponent, TranslateDirective, NgTemplateOutlet, ArtemisDatePipe, ArtemisTranslatePipe, MeetingPatternPipe], }) export class TutorialGroupRowComponent { readonly Math = Math; diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-groups-table.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-groups-table.component.ts index abcec9dd4824..cd667462b5da 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-groups-table.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-groups-table.component.ts @@ -1,17 +1,30 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Input, OnChanges, SimpleChanges, TemplateRef } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Input, OnChanges, SimpleChanges, TemplateRef, inject } from '@angular/core'; import { faQuestionCircle, faSort } from '@fortawesome/free-solid-svg-icons'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { SortService } from 'app/shared/service/sort.service'; import { Course } from 'app/entities/course.model'; import dayjs from 'dayjs/esm'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { RouterLink } from '@angular/router'; +import { TutorialGroupRowComponent } from './tutorial-group-row/tutorial-group-row.component'; +import { NgClass } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutorial-groups-table', templateUrl: './tutorial-groups-table.component.html', styleUrls: ['./tutorial-groups-table.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [TranslateDirective, SortDirective, SortByDirective, FaIconComponent, NgbTooltip, RouterLink, TutorialGroupRowComponent, NgClass, ArtemisTranslatePipe], }) export class TutorialGroupsTableComponent implements OnChanges { + private sortService = inject(SortService); + private cdr = inject(ChangeDetectorRef); + @ContentChild(TemplateRef, { static: true }) extraColumn: TemplateRef; @Input() @@ -50,11 +63,6 @@ export class TutorialGroupsTableComponent implements OnChanges { */ mifOfDifferentLanguages = false; - constructor( - private sortService: SortService, - private cdr: ChangeDetectorRef, - ) {} - trackId(index: number, item: TutorialGroup) { return item.id; } diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/registered-students/registered-students.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/registered-students/registered-students.component.ts index eafa6e8710ce..46ceddc68c67 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/registered-students/registered-students.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/registered-students/registered-students.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, inject } from '@angular/core'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { TutorialGroupsService } from 'app/course/tutorial-groups/services/tutorial-groups.service'; import { AlertService } from 'app/core/util/alert.service'; @@ -11,13 +11,24 @@ import { AccountService } from 'app/core/auth/account.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Subject } from 'rxjs'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CourseGroupComponent } from 'app/shared/course-group/course-group.component'; @Component({ selector: 'jhi-registered-students', templateUrl: './registered-students.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, CourseGroupComponent], }) export class RegisteredStudentsComponent implements OnDestroy { + private activeModal = inject(NgbActiveModal); + private tutorialGroupService = inject(TutorialGroupsService); + private alertService = inject(AlertService); + private accountService = inject(AccountService); + private courseService = inject(CourseManagementService); + private cdr = inject(ChangeDetectorRef); + @Input() course: Course; @@ -48,15 +59,6 @@ export class RegisteredStudentsComponent implements OnDestroy { } } - constructor( - private activeModal: NgbActiveModal, - private tutorialGroupService: TutorialGroupsService, - private alertService: AlertService, - private accountService: AccountService, - private courseService: CourseManagementService, - private cdr: ChangeDetectorRef, - ) {} - ngOnDestroy(): void { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/create-tutorial-group-free-period/create-tutorial-group-free-period.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/create-tutorial-group-free-period/create-tutorial-group-free-period.component.ts index 11c86fc7af6c..24258f9a6e5f 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/create-tutorial-group-free-period/create-tutorial-group-free-period.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/create-tutorial-group-free-period/create-tutorial-group-free-period.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, inject } from '@angular/core'; import { TutorialGroupFreePeriodDTO, TutorialGroupFreePeriodService } from 'app/course/tutorial-groups/services/tutorial-group-free-period.service'; import { TutorialGroupFreePeriodFormData } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/tutorial-free-period-form/tutorial-group-free-period-form.component'; import { finalize, takeUntil } from 'rxjs/operators'; @@ -9,13 +9,21 @@ import { Course } from 'app/entities/course.model'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Subject } from 'rxjs'; import { captureException } from '@sentry/angular'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TutorialGroupFreePeriodFormComponent } from '../tutorial-free-period-form/tutorial-group-free-period-form.component'; @Component({ selector: 'jhi-create-tutorial-group-free-day', templateUrl: './create-tutorial-group-free-period.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, TutorialGroupFreePeriodFormComponent], }) export class CreateTutorialGroupFreePeriodComponent implements OnDestroy { + private activeModal = inject(NgbActiveModal); + private tutorialGroupFreePeriodService = inject(TutorialGroupFreePeriodService); + private alertService = inject(AlertService); + ngUnsubscribe = new Subject(); tutorialGroupFreePeriodToCreate: TutorialGroupFreePeriodDTO = new TutorialGroupFreePeriodDTO(); @@ -29,12 +37,6 @@ export class CreateTutorialGroupFreePeriodComponent implements OnDestroy { isInitialized = false; - constructor( - private activeModal: NgbActiveModal, - private tutorialGroupFreePeriodService: TutorialGroupFreePeriodService, - private alertService: AlertService, - ) {} - initialize() { if (!this.tutorialGroupConfigurationId || !this.course) { console.error('Error: Component not fully configured'); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/edit-tutorial-group-free-period/edit-tutorial-group-free-period.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/edit-tutorial-group-free-period/edit-tutorial-group-free-period.component.ts index 2b345504a615..b9d7fa038e14 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/edit-tutorial-group-free-period/edit-tutorial-group-free-period.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/edit-tutorial-group-free-period/edit-tutorial-group-free-period.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, inject } from '@angular/core'; import { TutorialGroupsConfiguration } from 'app/entities/tutorial-group/tutorial-groups-configuration.model'; import { AlertService } from 'app/core/util/alert.service'; import { onError } from 'app/shared/util/global.utils'; @@ -12,13 +12,21 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { takeUntil } from 'rxjs/operators'; import { CreateTutorialGroupFreePeriodComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/create-tutorial-group-free-period/create-tutorial-group-free-period.component'; import { TutorialGroupFreePeriodsManagementComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-management.component'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TutorialGroupFreePeriodFormComponent } from '../tutorial-free-period-form/tutorial-group-free-period-form.component'; @Component({ selector: 'jhi-edit-tutorial-group-free-period', templateUrl: './edit-tutorial-group-free-period.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, TutorialGroupFreePeriodFormComponent], }) export class EditTutorialGroupFreePeriodComponent implements OnDestroy { + private activeModal = inject(NgbActiveModal); + private tutorialGroupFreePeriodService = inject(TutorialGroupFreePeriodService); + private alertService = inject(AlertService); + isLoading = false; @Input() @@ -34,11 +42,6 @@ export class EditTutorialGroupFreePeriodComponent implements OnDestroy { ngUnsubscribe = new Subject(); formData: TutorialGroupFreePeriodFormData; - constructor( - private activeModal: NgbActiveModal, - private tutorialGroupFreePeriodService: TutorialGroupFreePeriodService, - private alertService: AlertService, - ) {} /** * Initializes the component by setting up the form data based on the tutorial group free period, course, and tutorial groups configuration. diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/tutorial-free-period-form/tutorial-group-free-period-form.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/tutorial-free-period-form/tutorial-group-free-period-form.component.ts index 3bf9198265bd..f09b6861ef13 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/tutorial-free-period-form/tutorial-group-free-period-form.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/tutorial-free-period-form/tutorial-group-free-period-form.component.ts @@ -1,7 +1,12 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnInit, Output, inject } from '@angular/core'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { faCalendarAlt } from '@fortawesome/free-solid-svg-icons'; -import { OWL_DATE_TIME_FORMATS } from '@danielmoncada/angular-datetime-picker'; +import { OWL_DATE_TIME_FORMATS, OwlDateTimeModule } from '@danielmoncada/angular-datetime-picker'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export const MY_NATIVE_FORMATS = { datePickerInput: { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }, @@ -34,8 +39,11 @@ export enum TimeFrame { templateUrl: './tutorial-group-free-period-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush, providers: [{ provide: OWL_DATE_TIME_FORMATS, useValue: MY_NATIVE_FORMATS }], + imports: [TranslateDirective, FormsModule, ReactiveFormsModule, OwlDateTimeModule, NgClass, FaIconComponent, ArtemisDatePipe, ArtemisTranslatePipe], }) export class TutorialGroupFreePeriodFormComponent implements OnInit, OnChanges { + private fb = inject(FormBuilder); + @Input() formData: TutorialGroupFreePeriodFormData = { startDate: undefined, @@ -93,9 +101,21 @@ export class TutorialGroupFreePeriodFormComponent implements OnInit, OnChanges { */ get isStartBeforeEnd(): boolean { if (this.timeFrame === TimeFrame.PeriodWithinDay && this.endTimeControl?.value && this.startTimeControl?.value) { - return this.endTimeControl.value.setSeconds(0, 0) > this.startTimeControl.value.setSeconds(0, 0); + const endTime = new Date(this.endTimeControl.value.getTime()); + const startTime = new Date(this.startTimeControl.value.getTime()); + + endTime.setSeconds(0, 0); + startTime.setSeconds(0, 0); + + return endTime > startTime; } else if (this.timeFrame === TimeFrame.Period && this.endDateControl?.value && this.startDateControl?.value) { - return this.endDateControl.value.setHours(0, 0, 0, 0) > this.startDateControl.value.setHours(0, 0, 0, 0); + const endDate = new Date(this.endDateControl.value.getTime()); + const startDate = new Date(this.startDateControl.value.getTime()); + + endDate.setHours(0, 0, 0, 0); + startDate.setHours(0, 0, 0, 0); + + return endDate > startDate; } return true; } @@ -150,13 +170,11 @@ export class TutorialGroupFreePeriodFormComponent implements OnInit, OnChanges { return false; } - constructor(private fb: FormBuilder) {} - ngOnInit(): void { this.initializeForm(); } - ngOnChanges(): void { + ngOnChanges() { this.initializeForm(); if (this.isEditMode && this.formData) { this.setFormValues(this.formData); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-period-row-buttons/tutorial-group-free-period-row-buttons.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-period-row-buttons/tutorial-group-free-period-row-buttons.component.ts index c841ff877284..9354d920a7fc 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-period-row-buttons/tutorial-group-free-period-row-buttons.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-period-row-buttons/tutorial-group-free-period-row-buttons.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output, inject } from '@angular/core'; import { TutorialGroupFreePeriod } from 'app/entities/tutorial-group/tutorial-group-free-day.model'; import { TutorialGroupFreePeriodService } from 'app/course/tutorial-groups/services/tutorial-group-free-period.service'; import { HttpErrorResponse } from '@angular/common/http'; @@ -9,13 +9,21 @@ import { Course } from 'app/entities/course.model'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { EditTutorialGroupFreePeriodComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/edit-tutorial-group-free-period/edit-tutorial-group-free-period.component'; import { catchError, takeUntil } from 'rxjs/operators'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-tutorial-group-free-period-row-buttons', templateUrl: './tutorial-group-free-period-row-buttons.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [FaIconComponent, TranslateDirective, DeleteButtonDirective, ArtemisDatePipe], }) export class TutorialGroupFreePeriodRowButtonsComponent implements OnDestroy { + private tutorialGroupFreePeriodService = inject(TutorialGroupFreePeriodService); + private modalService = inject(NgbModal); + @Input() course: Course; @Input() tutorialGroupConfiguration: TutorialGroupsConfiguration; @Input() tutorialFreePeriod: TutorialGroupFreePeriod; @@ -31,11 +39,6 @@ export class TutorialGroupFreePeriodRowButtonsComponent implements OnDestroy { faUsers = faUsers; faTrash = faTrash; - constructor( - private tutorialGroupFreePeriodService: TutorialGroupFreePeriodService, - private modalService: NgbModal, - ) {} - deleteTutorialFreePeriod = () => { this.tutorialGroupFreePeriodService .delete(this.course.id!, this.tutorialGroupConfiguration.id!, this.tutorialFreePeriod.id!) diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-management.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-management.component.ts index 9dffbe4b5727..a27bb05b3c0d 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-management.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-management.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { onError } from 'app/shared/util/global.utils'; import { TutorialGroupsConfiguration } from 'app/entities/tutorial-group/tutorial-groups-configuration.model'; import { EMPTY, Subject, combineLatest, finalize, from, switchMap, take } from 'rxjs'; @@ -14,14 +14,27 @@ import dayjs from 'dayjs/esm'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { CreateTutorialGroupFreePeriodComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/crud/create-tutorial-group-free-period/create-tutorial-group-free-period.component'; import { catchError, takeUntil } from 'rxjs/operators'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TutorialGroupFreePeriodsTableComponent } from './tutorial-group-free-periods-table/tutorial-group-free-periods-table.component'; @Component({ selector: 'jhi-tutorial-free-periods', templateUrl: './tutorial-group-free-periods-management.component.html', styleUrls: ['./tutorial-group-free-periods-management.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, FaIconComponent, TutorialGroupFreePeriodsTableComponent], }) export class TutorialGroupFreePeriodsManagementComponent implements OnInit, OnDestroy { + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private tutorialGroupsConfigurationService = inject(TutorialGroupsConfigurationService); + private alertService = inject(AlertService); + private sortService = inject(SortService); + private modalService = inject(NgbModal); + private cdr = inject(ChangeDetectorRef); + isLoading = false; tutorialGroupsConfiguration: TutorialGroupsConfiguration; tutorialGroupFreePeriods: TutorialGroupFreePeriod[] = []; @@ -34,16 +47,6 @@ export class TutorialGroupFreePeriodsManagementComponent implements OnInit, OnDe ngUnsubscribe = new Subject(); - constructor( - private activatedRoute: ActivatedRoute, - private router: Router, - private tutorialGroupsConfigurationService: TutorialGroupsConfigurationService, - private alertService: AlertService, - private sortService: SortService, - private modalService: NgbModal, - private cdr: ChangeDetectorRef, - ) {} - ngOnInit(): void { this.loadAll(); } diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-table/tutorial-group-free-periods-table.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-table/tutorial-group-free-periods-table.component.ts index e662adb9bffe..2a70480390fb 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-table/tutorial-group-free-periods-table.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-table/tutorial-group-free-periods-table.component.ts @@ -4,12 +4,18 @@ import { Course } from 'app/entities/course.model'; import { TutorialGroupsConfiguration } from 'app/entities/tutorial-group/tutorial-groups-configuration.model'; import dayjs from 'dayjs/esm'; import { TutorialGroupFreePeriodsManagementComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-management.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass } from '@angular/common'; +import { TutorialGroupFreePeriodRowButtonsComponent } from '../tutorial-group-free-period-row-buttons/tutorial-group-free-period-row-buttons.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutorial-group-free-periods-table', templateUrl: './tutorial-group-free-periods-table.component.html', styleUrls: ['../tutorial-group-free-periods-management.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [TranslateDirective, NgClass, TutorialGroupFreePeriodRowButtonsComponent, ArtemisDatePipe, ArtemisTranslatePipe], }) export class TutorialGroupFreePeriodsTableComponent { @Input() course: Course; diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-management-resolve.service.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-management-resolve.service.ts index 862c1bfae9b4..55c86c31eaf1 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-management-resolve.service.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-management-resolve.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@angular/router'; import { Course } from 'app/entities/course.model'; import { CourseManagementService } from 'app/course/manage/course-management.service'; @@ -8,10 +8,8 @@ import { tap } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class TutorialGroupManagementResolve implements Resolve { - constructor( - private service: CourseManagementService, - private router: Router, - ) {} + private service = inject(CourseManagementService); + private router = inject(Router); resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { return this.service.find(route.params['courseId']).pipe( diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/create-tutorial-group-session/create-tutorial-group-session.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/create-tutorial-group-session/create-tutorial-group-session.component.ts index 36158766cdaa..0da7cb2344fc 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/create-tutorial-group-session/create-tutorial-group-session.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/create-tutorial-group-session/create-tutorial-group-session.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, inject } from '@angular/core'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { AlertService } from 'app/core/util/alert.service'; import { finalize, takeUntil } from 'rxjs/operators'; @@ -8,13 +8,21 @@ import { TutorialGroupSessionDTO, TutorialGroupSessionService } from 'app/course import { Course } from 'app/entities/course.model'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Subject } from 'rxjs'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TutorialGroupSessionFormComponent } from '../tutorial-group-session-form/tutorial-group-session-form.component'; @Component({ selector: 'jhi-create-tutorial-group-session', templateUrl: './create-tutorial-group-session.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, TutorialGroupSessionFormComponent], }) export class CreateTutorialGroupSessionComponent implements OnDestroy { + private activeModal = inject(NgbActiveModal); + private tutorialGroupSessionService = inject(TutorialGroupSessionService); + private alertService = inject(AlertService); + ngUnsubscribe = new Subject(); tutorialGroupSessionToCreate: TutorialGroupSessionDTO = new TutorialGroupSessionDTO(); @@ -28,12 +36,6 @@ export class CreateTutorialGroupSessionComponent implements OnDestroy { isInitialized = false; - constructor( - private activeModal: NgbActiveModal, - private tutorialGroupSessionService: TutorialGroupSessionService, - private alertService: AlertService, - ) {} - initialize() { if (!this.course || !this.tutorialGroup) { console.error('Error: Component not fully configured'); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/edit-tutorial-group-session/edit-tutorial-group-session.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/edit-tutorial-group-session/edit-tutorial-group-session.component.ts index 974395b1ff47..1f6383bfa8f1 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/edit-tutorial-group-session/edit-tutorial-group-session.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/edit-tutorial-group-session/edit-tutorial-group-session.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Input, OnDestroy } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, inject } from '@angular/core'; import { TutorialGroupSession } from 'app/entities/tutorial-group/tutorial-group-session.model'; import { TutorialGroupSessionFormData } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/tutorial-group-session-form/tutorial-group-session-form.component'; import { AlertService } from 'app/core/util/alert.service'; @@ -9,13 +9,21 @@ import { Course } from 'app/entities/course.model'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Subject } from 'rxjs'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TutorialGroupSessionFormComponent } from '../tutorial-group-session-form/tutorial-group-session-form.component'; @Component({ selector: 'jhi-edit-tutorial-group-session', templateUrl: './edit-tutorial-group-session.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, TutorialGroupSessionFormComponent], }) export class EditTutorialGroupSessionComponent implements OnDestroy { + private activeModal = inject(NgbActiveModal); + private tutorialGroupSessionService = inject(TutorialGroupSessionService); + private alertService = inject(AlertService); + ngUnsubscribe = new Subject(); @Input() @@ -32,12 +40,6 @@ export class EditTutorialGroupSessionComponent implements OnDestroy { isInitialized = false; - constructor( - private activeModal: NgbActiveModal, - private tutorialGroupSessionService: TutorialGroupSessionService, - private alertService: AlertService, - ) {} - initialize() { if (!this.tutorialGroupSession || !this.course || !this.tutorialGroup) { console.error('Error: Component not fully configured'); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/tutorial-group-session-form/tutorial-group-session-form.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/tutorial-group-session-form/tutorial-group-session-form.component.ts index 8a4c7e1ded95..52c7b6d1981d 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/tutorial-group-session-form/tutorial-group-session-form.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/tutorial-group-session-form/tutorial-group-session-form.component.ts @@ -1,9 +1,14 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { NgbTimeAdapter } from '@ng-bootstrap/ng-bootstrap'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnInit, Output, inject } from '@angular/core'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import { NgbTimeAdapter, NgbTimepicker } from '@ng-bootstrap/ng-bootstrap'; import { faCalendarAlt } from '@fortawesome/free-solid-svg-icons'; import { NgbTimeStringAdapter } from 'app/course/tutorial-groups/shared/ngbTimeStringAdapter'; import { validTimeRange } from 'app/course/tutorial-groups/shared/timeRangeValidator'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { OwlDateTimeModule } from '@danielmoncada/angular-datetime-picker'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export interface TutorialGroupSessionFormData { date?: Date; @@ -17,8 +22,11 @@ export interface TutorialGroupSessionFormData { templateUrl: './tutorial-group-session-form.component.html', providers: [{ provide: NgbTimeAdapter, useClass: NgbTimeStringAdapter }], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [TranslateDirective, FormsModule, ReactiveFormsModule, OwlDateTimeModule, FaIconComponent, NgbTimepicker, ArtemisDatePipe, ArtemisTranslatePipe], }) export class TutorialGroupSessionFormComponent implements OnInit, OnChanges { + private fb = inject(FormBuilder); + @Input() formData: TutorialGroupSessionFormData = { date: undefined, @@ -67,13 +75,11 @@ export class TutorialGroupSessionFormComponent implements OnInit, OnChanges { return !this.form.invalid; } - constructor(private fb: FormBuilder) {} - ngOnInit(): void { this.initializeForm(); } - ngOnChanges(): void { + ngOnChanges() { this.initializeForm(); if (this.isEditMode && this.formData) { this.setFormValues(this.formData); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/cancellation-modal/cancellation-modal.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/cancellation-modal/cancellation-modal.component.ts index 912ecaeb810e..f600c8ba859e 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/cancellation-modal/cancellation-modal.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/cancellation-modal/cancellation-modal.component.ts @@ -1,21 +1,29 @@ -import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { TutorialGroupSession, TutorialGroupSessionStatus } from 'app/entities/tutorial-group/tutorial-group-session.model'; import { onError } from 'app/shared/util/global.utils'; import { TutorialGroupSessionService } from 'app/course/tutorial-groups/services/tutorial-group-session.service'; import { HttpErrorResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { Course } from 'app/entities/course.model'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-cancellation-modal', templateUrl: './cancellation-modal.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [FormsModule, ReactiveFormsModule, TranslateDirective, ArtemisTranslatePipe], }) export class CancellationModalComponent implements OnInit, OnDestroy { + activeModal = inject(NgbActiveModal); + private tutorialGroupSessionService = inject(TutorialGroupSessionService); + private alertService = inject(AlertService); + private fb = inject(FormBuilder); + ngUnsubscribe = new Subject(); tutorialGroupSessionStatus = TutorialGroupSessionStatus; @@ -30,13 +38,6 @@ export class CancellationModalComponent implements OnInit, OnDestroy { @Input() tutorialGroupSession: TutorialGroupSession; - constructor( - public activeModal: NgbActiveModal, - private tutorialGroupSessionService: TutorialGroupSessionService, - private alertService: AlertService, - private fb: FormBuilder, - ) {} - ngOnInit(): void { this.initializeForm(); } diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/tutorial-group-session-row-buttons/tutorial-group-session-row-buttons.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/tutorial-group-session-row-buttons/tutorial-group-session-row-buttons.component.ts index 3e037d4c89b6..916f8889b70d 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/tutorial-group-session-row-buttons/tutorial-group-session-row-buttons.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/tutorial-group-session-row-buttons/tutorial-group-session-row-buttons.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output, inject } from '@angular/core'; import { EMPTY, Subject, from } from 'rxjs'; import { faTrash, faUsers, faWrench } from '@fortawesome/free-solid-svg-icons'; import { TutorialGroupSessionService } from 'app/course/tutorial-groups/services/tutorial-group-session.service'; @@ -10,13 +10,22 @@ import { Course } from 'app/entities/course.model'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { EditTutorialGroupSessionComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/edit-tutorial-group-session/edit-tutorial-group-session.component'; import { catchError, takeUntil } from 'rxjs/operators'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutorial-group-session-row-buttons', templateUrl: './tutorial-group-session-row-buttons.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [FaIconComponent, TranslateDirective, DeleteButtonDirective, ArtemisDatePipe, ArtemisTranslatePipe], }) export class TutorialGroupSessionRowButtonsComponent implements OnDestroy { + private tutorialGroupSessionService = inject(TutorialGroupSessionService); + private modalService = inject(NgbModal); + ngUnsubscribe = new Subject(); @Input() course: Course; @@ -35,11 +44,6 @@ export class TutorialGroupSessionRowButtonsComponent implements OnDestroy { faUsers = faUsers; faTrash = faTrash; - constructor( - private tutorialGroupSessionService: TutorialGroupSessionService, - private modalService: NgbModal, - ) {} - deleteTutorialGroupSession = () => { this.tutorialGroupSessionService .delete(this.course.id!, this.tutorialGroup.id!, this.tutorialGroupSession.id!) diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/tutorial-group-sessions-management.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/tutorial-group-sessions-management.component.ts index 4dc520b817c9..4cc9264d423c 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/tutorial-group-sessions-management.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/tutorial-group-sessions-management.component.ts @@ -1,6 +1,7 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, ViewEncapsulation, inject } from '@angular/core'; import { TutorialGroupsService } from 'app/course/tutorial-groups/services/tutorial-groups.service'; import { AlertService } from 'app/core/util/alert.service'; +import { TutorialGroupSessionsTableComponent } from 'app/course/tutorial-groups/shared/tutorial-group-sessions-table/tutorial-group-sessions-table.component'; import { EMPTY, Subject, from } from 'rxjs'; import { catchError, finalize, map, takeUntil } from 'rxjs/operators'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; @@ -13,6 +14,12 @@ import { TutorialGroupSession } from 'app/entities/tutorial-group/tutorial-group import { getDayTranslationKey } from 'app/course/tutorial-groups/shared/weekdays'; import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { CreateTutorialGroupSessionComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/crud/create-tutorial-group-session/create-tutorial-group-session.component'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TutorialGroupSessionRowButtonsComponent } from './tutorial-group-session-row-buttons/tutorial-group-session-row-buttons.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { RemoveSecondsPipe } from 'app/course/tutorial-groups/shared/remove-seconds.pipe'; @Component({ selector: 'jhi-session-management', @@ -20,8 +27,23 @@ import { CreateTutorialGroupSessionComponent } from 'app/course/tutorial-groups/ styleUrls: ['./tutorial-group-sessions-management.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, + imports: [ + LoadingIndicatorContainerComponent, + TranslateDirective, + FaIconComponent, + TutorialGroupSessionsTableComponent, + TutorialGroupSessionRowButtonsComponent, + ArtemisTranslatePipe, + RemoveSecondsPipe, + ], }) export class TutorialGroupSessionsManagementComponent implements OnDestroy { + private tutorialGroupService = inject(TutorialGroupsService); + private alertService = inject(AlertService); + private modalService = inject(NgbModal); + private activeModal = inject(NgbActiveModal); + private cdr = inject(ChangeDetectorRef); + ngUnsubscribe = new Subject(); isLoading = false; @@ -39,14 +61,6 @@ export class TutorialGroupSessionsManagementComponent implements OnDestroy { isInitialized = false; - constructor( - private tutorialGroupService: TutorialGroupsService, - private alertService: AlertService, - private modalService: NgbModal, - private activeModal: NgbActiveModal, - private cdr: ChangeDetectorRef, - ) {} - initialize() { if (!this.tutorialGroupId || !this.course) { console.error('Error: Component not fully configured'); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-checklist/tutorial-groups-checklist.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-checklist/tutorial-groups-checklist.component.ts index 8f18bbb4a7d4..aa6a810aa7a7 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-checklist/tutorial-groups-checklist.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-checklist/tutorial-groups-checklist.component.ts @@ -1,5 +1,5 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { Course } from 'app/entities/course.model'; import { AlertService } from 'app/core/util/alert.service'; @@ -9,13 +9,24 @@ import { HttpErrorResponse } from '@angular/common/http'; import { faPlus, faWrench } from '@fortawesome/free-solid-svg-icons'; import { TutorialGroupsConfigurationService } from 'app/course/tutorial-groups/services/tutorial-groups-configuration.service'; import { takeUntil } from 'rxjs/operators'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ChecklistCheckComponent } from 'app/shared/components/checklist-check/checklist-check.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-tutorial-groups-checklist', templateUrl: './tutorial-groups-checklist.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, ChecklistCheckComponent, RouterLink, FaIconComponent], }) export class TutorialGroupsChecklistComponent implements OnInit, OnDestroy { + private activatedRoute = inject(ActivatedRoute); + private courseManagementService = inject(CourseManagementService); + private alertService = inject(AlertService); + private tutorialGroupsConfigurationService = inject(TutorialGroupsConfigurationService); + private cdr = inject(ChangeDetectorRef); + isLoading = false; course: Course; isTimeZoneConfigured = false; @@ -25,13 +36,6 @@ export class TutorialGroupsChecklistComponent implements OnInit, OnDestroy { faPlus = faPlus; ngUnsubscribe = new Subject(); - constructor( - private activatedRoute: ActivatedRoute, - private courseManagementService: CourseManagementService, - private alertService: AlertService, - private tutorialGroupsConfigurationService: TutorialGroupsConfigurationService, - private cdr: ChangeDetectorRef, - ) {} get isFullyConfigured(): boolean { return this.isTimeZoneConfigured && this.isTutorialGroupConfigurationCreated; diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/create-tutorial-groups-configuration/create-tutorial-groups-configuration.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/create-tutorial-groups-configuration/create-tutorial-groups-configuration.component.ts index 9686406a47e2..36392e841ab7 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/create-tutorial-groups-configuration/create-tutorial-groups-configuration.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/create-tutorial-groups-configuration/create-tutorial-groups-configuration.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { AlertService } from 'app/core/util/alert.service'; import { ActivatedRoute, Router } from '@angular/router'; import { TutorialGroupsConfiguration } from 'app/entities/tutorial-group/tutorial-groups-configuration.model'; @@ -11,30 +11,31 @@ import { CourseManagementService } from 'app/course/manage/course-management.ser import { Course } from 'app/entities/course.model'; import { Subject } from 'rxjs'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TutorialGroupsConfigurationFormComponent } from '../tutorial-groups-configuration-form/tutorial-groups-configuration-form.component'; @Component({ selector: 'jhi-create-tutorial-groups-configuration', templateUrl: './create-tutorial-groups-configuration.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, TutorialGroupsConfigurationFormComponent], }) export class CreateTutorialGroupsConfigurationComponent implements OnInit, OnDestroy { + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private tutorialGroupsConfigurationService = inject(TutorialGroupsConfigurationService); + private courseManagementService = inject(CourseManagementService); + private alertService = inject(AlertService); + private courseStorageService = inject(CourseStorageService); + private cdr = inject(ChangeDetectorRef); + ngUnsubscribe = new Subject(); newTutorialGroupsConfiguration = new TutorialGroupsConfiguration(); isLoading: boolean; course: Course; - constructor( - private activatedRoute: ActivatedRoute, - private router: Router, - private tutorialGroupsConfigurationService: TutorialGroupsConfigurationService, - private courseManagementService: CourseManagementService, - private alertService: AlertService, - private courseStorageService: CourseStorageService, - - private cdr: ChangeDetectorRef, - ) {} - ngOnInit(): void { this.isLoading = true; this.activatedRoute.paramMap diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/edit-tutorial-groups-configuration/edit-tutorial-groups-configuration.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/edit-tutorial-groups-configuration/edit-tutorial-groups-configuration.component.ts index 0998e7060f03..f34a351447d3 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/edit-tutorial-groups-configuration/edit-tutorial-groups-configuration.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/edit-tutorial-groups-configuration/edit-tutorial-groups-configuration.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { TutorialGroupsConfiguration } from 'app/entities/tutorial-group/tutorial-groups-configuration.model'; import { TutorialGroupsConfigurationFormData } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/tutorial-groups-configuration-form/tutorial-groups-configuration-form.component'; import { AlertService } from 'app/core/util/alert.service'; @@ -10,13 +10,24 @@ import { finalize, switchMap, take, takeUntil } from 'rxjs/operators'; import { HttpErrorResponse } from '@angular/common/http'; import { Course } from 'app/entities/course.model'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TutorialGroupsConfigurationFormComponent } from '../tutorial-groups-configuration-form/tutorial-groups-configuration-form.component'; @Component({ selector: 'jhi-edit-tutorial-groups-configuration', templateUrl: './edit-tutorial-groups-configuration.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, TutorialGroupsConfigurationFormComponent], }) export class EditTutorialGroupsConfigurationComponent implements OnInit, OnDestroy { + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private tutorialGroupsConfigurationService = inject(TutorialGroupsConfigurationService); + private courseStorageService = inject(CourseStorageService); + private alertService = inject(AlertService); + private cdr = inject(ChangeDetectorRef); + ngUnsubscribe = new Subject(); isLoading = false; @@ -25,16 +36,6 @@ export class EditTutorialGroupsConfigurationComponent implements OnInit, OnDestr course: Course; tutorialGroupConfigurationId: number; - constructor( - private activatedRoute: ActivatedRoute, - private router: Router, - private tutorialGroupsConfigurationService: TutorialGroupsConfigurationService, - private courseStorageService: CourseStorageService, - private alertService: AlertService, - - private cdr: ChangeDetectorRef, - ) {} - ngOnInit(): void { this.isLoading = true; combineLatest([this.activatedRoute.paramMap, this.activatedRoute.data]) diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/tutorial-groups-configuration-form/tutorial-groups-configuration-form.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/tutorial-groups-configuration-form/tutorial-groups-configuration-form.component.ts index 65309727c283..4c6ec6bedc7c 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/tutorial-groups-configuration-form/tutorial-groups-configuration-form.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/tutorial-groups-configuration-form/tutorial-groups-configuration-form.component.ts @@ -1,7 +1,11 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnInit, Output, inject } from '@angular/core'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { faCalendarAlt } from '@fortawesome/free-solid-svg-icons'; import { Course, isMessagingEnabled } from 'app/entities/course.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { OwlDateTimeModule } from '@danielmoncada/angular-datetime-picker'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDateRangePipe } from 'app/shared/pipes/artemis-date-range.pipe'; export interface TutorialGroupsConfigurationFormData { period?: Date[]; @@ -14,8 +18,11 @@ export interface TutorialGroupsConfigurationFormData { templateUrl: './tutorial-groups-configuration-form.component.html', styleUrls: ['./tutorial-groups-configuration-form.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [FormsModule, ReactiveFormsModule, TranslateDirective, OwlDateTimeModule, FaIconComponent, ArtemisDateRangePipe], }) export class TutorialGroupsConfigurationFormComponent implements OnInit, OnChanges { + private fb = inject(FormBuilder); + @Input() formData: TutorialGroupsConfigurationFormData = { period: undefined, @@ -36,8 +43,6 @@ export class TutorialGroupsConfigurationFormComponent implements OnInit, OnChang form: FormGroup; - constructor(private fb: FormBuilder) {} - get periodControl() { return this.form.get('period'); } @@ -57,7 +62,7 @@ export class TutorialGroupsConfigurationFormComponent implements OnInit, OnChang ngOnInit(): void { this.initializeForm(); } - ngOnChanges(): void { + ngOnChanges() { this.initializeForm(); if (this.isEditMode && this.formData) { this.setFormValues(this.formData); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.module.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.module.ts index aaeac91ae070..6f952dcbf621 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.module.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.module.ts @@ -53,8 +53,6 @@ import { ArtemisMarkdownEditorModule } from 'app/shared/markdown-editor/markdown ArtemisSidePanelModule, ArtemisSharedComponentModule, ArtemisMarkdownEditorModule, - ], - declarations: [ TutorialGroupsManagementComponent, TutorialGroupFormComponent, CreateTutorialGroupComponent, diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.route.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.route.ts index 2b48dcfd4a48..c35051014537 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.route.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-management.route.ts @@ -3,18 +3,16 @@ */ import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { TutorialGroupFreePeriodsManagementComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-management.component'; -import { TutorialGroupManagementDetailComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/detail/tutorial-group-management-detail.component'; -import { TutorialGroupsManagementComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-management.component'; -import { EditTutorialGroupComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/edit-tutorial-group/edit-tutorial-group.component'; -import { CreateTutorialGroupComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/create-tutorial-group/create-tutorial-group.component'; -import { EditTutorialGroupsConfigurationComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/edit-tutorial-groups-configuration/edit-tutorial-groups-configuration.component'; + import { Routes } from '@angular/router'; export const tutorialGroupManagementRoutes: Routes = [ { path: '', - component: TutorialGroupsManagementComponent, + loadComponent: () => + import('app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-management.component').then( + (m) => m.TutorialGroupsManagementComponent, + ), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.pages.tutorialGroupsManagement.title', @@ -23,7 +21,10 @@ export const tutorialGroupManagementRoutes: Routes = [ }, { path: 'configuration', - component: TutorialGroupsManagementComponent, + loadComponent: () => + import('app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-management.component').then( + (m) => m.TutorialGroupsManagementComponent, + ), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.pages.tutorialGroupsManagement.title', @@ -32,7 +33,10 @@ export const tutorialGroupManagementRoutes: Routes = [ }, { path: 'configuration/:tutorialGroupsConfigurationId/edit', - component: EditTutorialGroupsConfigurationComponent, + loadComponent: () => + import( + 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups-configuration/crud/edit-tutorial-groups-configuration/edit-tutorial-groups-configuration.component' + ).then((m) => m.EditTutorialGroupsConfigurationComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.pages.editTutorialGroupsConfiguration.title', @@ -41,7 +45,10 @@ export const tutorialGroupManagementRoutes: Routes = [ }, { path: 'configuration/:tutorialGroupsConfigurationId/tutorial-free-days', - component: TutorialGroupFreePeriodsManagementComponent, + loadComponent: () => + import( + 'app/course/tutorial-groups/tutorial-groups-management/tutorial-free-periods/tutorial-free-periods-management/tutorial-group-free-periods-management.component' + ).then((m) => m.TutorialGroupFreePeriodsManagementComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.pages.tutorialFreePeriodsManagement.title', @@ -50,7 +57,10 @@ export const tutorialGroupManagementRoutes: Routes = [ }, { path: 'create', - component: CreateTutorialGroupComponent, + loadComponent: () => + import('app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/create-tutorial-group/create-tutorial-group.component').then( + (m) => m.CreateTutorialGroupComponent, + ), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.pages.createTutorialGroup.title', @@ -59,7 +69,10 @@ export const tutorialGroupManagementRoutes: Routes = [ }, { path: ':tutorialGroupId/edit', - component: EditTutorialGroupComponent, + loadComponent: () => + import('app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/edit-tutorial-group/edit-tutorial-group.component').then( + (m) => m.EditTutorialGroupComponent, + ), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.pages.editTutorialGroup.title', @@ -68,7 +81,10 @@ export const tutorialGroupManagementRoutes: Routes = [ }, { path: ':tutorialGroupId', - component: TutorialGroupManagementDetailComponent, + loadComponent: () => + import('app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/detail/tutorial-group-management-detail.component').then( + (m) => m.TutorialGroupManagementDetailComponent, + ), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.pages.tutorialGroupDetail.title', diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/create-tutorial-group/create-tutorial-group.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/create-tutorial-group/create-tutorial-group.component.ts index 8d08d128296f..71d51d5c1247 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/create-tutorial-group/create-tutorial-group.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/create-tutorial-group/create-tutorial-group.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { AlertService } from 'app/core/util/alert.service'; import { ActivatedRoute, Router } from '@angular/router'; import { TutorialGroupsService } from 'app/course/tutorial-groups/services/tutorial-groups.service'; @@ -11,24 +11,27 @@ import { TutorialGroupSchedule } from 'app/entities/tutorial-group/tutorial-grou import dayjs from 'dayjs/esm'; import { Course } from 'app/entities/course.model'; import { Subject } from 'rxjs'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TutorialGroupFormComponent } from '../tutorial-group-form/tutorial-group-form.component'; @Component({ selector: 'jhi-create-tutorial-group', templateUrl: './create-tutorial-group.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, TutorialGroupFormComponent], }) export class CreateTutorialGroupComponent implements OnInit, OnDestroy { + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private tutorialGroupService = inject(TutorialGroupsService); + private alertService = inject(AlertService); + tutorialGroupToCreate: TutorialGroup = new TutorialGroup(); isLoading: boolean; course: Course; ngUnsubscribe = new Subject(); - constructor( - private activatedRoute: ActivatedRoute, - private router: Router, - private tutorialGroupService: TutorialGroupsService, - private alertService: AlertService, - ) {} ngOnInit(): void { this.activatedRoute.data.pipe(takeUntil(this.ngUnsubscribe)).subscribe(({ course }) => { diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/edit-tutorial-group/edit-tutorial-group.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/edit-tutorial-group/edit-tutorial-group.component.ts index 0d8d24df7b38..2be9e266f3a5 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/edit-tutorial-group/edit-tutorial-group.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/edit-tutorial-group/edit-tutorial-group.component.ts @@ -1,7 +1,7 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; -import { TutorialGroupFormData } from '../tutorial-group-form/tutorial-group-form.component'; +import { TutorialGroupFormComponent, TutorialGroupFormData } from '../tutorial-group-form/tutorial-group-form.component'; import { onError } from 'app/shared/util/global.utils'; import { Subject, combineLatest } from 'rxjs'; import { finalize, switchMap, take, takeUntil } from 'rxjs/operators'; @@ -11,13 +11,22 @@ import { AlertService } from 'app/core/util/alert.service'; import { TutorialGroupSchedule } from 'app/entities/tutorial-group/tutorial-group-schedule.model'; import dayjs from 'dayjs/esm'; import { Course } from 'app/entities/course.model'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-edit-tutorial-group', templateUrl: './edit-tutorial-group.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TranslateDirective, TutorialGroupFormComponent], }) export class EditTutorialGroupComponent implements OnInit, OnDestroy { + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private tutorialGroupService = inject(TutorialGroupsService); + private alertService = inject(AlertService); + private cdr = inject(ChangeDetectorRef); + ngUnsubscribe = new Subject(); isLoading = false; @@ -27,14 +36,6 @@ export class EditTutorialGroupComponent implements OnInit, OnDestroy { tutorialGroupId: number; course: Course; - constructor( - private activatedRoute: ActivatedRoute, - private router: Router, - private tutorialGroupService: TutorialGroupsService, - private alertService: AlertService, - private cdr: ChangeDetectorRef, - ) {} - ngOnInit(): void { this.isLoading = true; combineLatest([this.activatedRoute.paramMap, this.activatedRoute.data]) diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/schedule-form/schedule-form.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/schedule-form/schedule-form.component.ts index 31e3b2bcc1c6..f81db6d68366 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/schedule-form/schedule-form.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/schedule-form/schedule-form.component.ts @@ -1,7 +1,7 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, inject } from '@angular/core'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { faCalendarAlt } from '@fortawesome/free-solid-svg-icons'; -import { NgbDateParserFormatter, NgbTimeAdapter } from '@ng-bootstrap/ng-bootstrap'; +import { NgbDateParserFormatter, NgbTimeAdapter, NgbTimepicker } from '@ng-bootstrap/ng-bootstrap'; import { weekDays } from 'app/course/tutorial-groups/shared/weekdays'; import { Course } from 'app/entities/course.model'; import * as _ from 'lodash-es'; @@ -9,6 +9,12 @@ import dayjs from 'dayjs/esm'; import { dayOfWeekZeroSundayToZeroMonday } from 'app/utils/date.utils'; import { NgbTimeStringAdapter } from 'app/course/tutorial-groups/shared/ngbTimeStringAdapter'; import { validTimeRange } from 'app/course/tutorial-groups/shared/timeRangeValidator'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { OwlDateTimeModule } from '@danielmoncada/angular-datetime-picker'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisDateRangePipe } from 'app/shared/pipes/artemis-date-range.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export interface ScheduleFormData { dayOfWeek?: number; @@ -25,8 +31,13 @@ export interface ScheduleFormData { styleUrls: ['./schedule-form.component.scss'], providers: [{ provide: NgbTimeAdapter, useClass: NgbTimeStringAdapter }], changeDetection: ChangeDetectionStrategy.OnPush, + imports: [FormsModule, ReactiveFormsModule, TranslateDirective, NgbTimepicker, OwlDateTimeModule, FaIconComponent, ArtemisDatePipe, ArtemisDateRangePipe, ArtemisTranslatePipe], }) export class ScheduleFormComponent implements OnInit { + private fb = inject(FormBuilder); + formatter = inject(NgbDateParserFormatter); + cdr = inject(ChangeDetectorRef); + @Input() course: Course; @Input() parentFormGroup: FormGroup; formGroup: FormGroup; @@ -87,12 +98,6 @@ export class ScheduleFormComponent implements OnInit { return sessions; } - constructor( - private fb: FormBuilder, - public formatter: NgbDateParserFormatter, - public cdr: ChangeDetectorRef, - ) {} - ngOnInit(): void { if (this.course.tutorialGroupsConfiguration) { const { tutorialPeriodStartInclusive, tutorialPeriodEndInclusive } = this.course.tutorialGroupsConfiguration; diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/tutorial-group-form.component.html b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/tutorial-group-form.component.html index 881abff7acd5..b3edd62913e1 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/tutorial-group-form.component.html +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/tutorial-group-form.component.html @@ -163,7 +163,7 @@
    - +
    @if (showScheduledChangedWarning) { diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/tutorial-group-form.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/tutorial-group-form.component.ts index c4d619a8869d..11c61a3acbd8 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/tutorial-group-form.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/tutorial-group-form.component.ts @@ -1,5 +1,5 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, ViewChild, inject } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { CourseManagementService } from 'app/course/manage/course-management.service'; import { Course, CourseGroup } from 'app/entities/course.model'; import { User } from 'app/core/user/user.model'; @@ -16,6 +16,10 @@ import { import { isEqual } from 'lodash-es'; import { TutorialGroupsService } from 'app/course/tutorial-groups/services/tutorial-groups.service'; import { faSave } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export interface TutorialGroupFormData { title?: string; @@ -40,8 +44,14 @@ export const titleRegex = new RegExp('^[a-zA-Z0-9]{1}[a-zA-Z0-9- ]{0,19}$'); selector: 'jhi-tutorial-group-form', templateUrl: './tutorial-group-form.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [FormsModule, ReactiveFormsModule, TranslateDirective, NgbTypeahead, MarkdownEditorMonacoComponent, ScheduleFormComponent, FaIconComponent, ArtemisTranslatePipe], }) export class TutorialGroupFormComponent implements OnInit, OnChanges, OnDestroy { + private fb = inject(FormBuilder); + private courseManagementService = inject(CourseManagementService); + private tutorialGroupService = inject(TutorialGroupsService); + private alertService = inject(AlertService); + @Input() formData: TutorialGroupFormData = { title: undefined, @@ -89,13 +99,6 @@ export class TutorialGroupFormComponent implements OnInit, OnChanges, OnDestroy ngUnsubscribe = new Subject(); - constructor( - private fb: FormBuilder, - private courseManagementService: CourseManagementService, - private tutorialGroupService: TutorialGroupsService, - private alertService: AlertService, - ) {} - get titleControl() { return this.form.get('title'); } @@ -196,7 +199,7 @@ export class TutorialGroupFormComponent implements OnInit, OnChanges, OnDestroy this.ngUnsubscribe.complete(); } - ngOnChanges(): void { + ngOnChanges() { this.initializeForm(); if (this.isEditMode && this.formData) { this.setFormValues(this.formData); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/detail/tutorial-group-management-detail.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/detail/tutorial-group-management-detail.component.ts index 010ae59754e1..1c10cda81c6d 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/detail/tutorial-group-management-detail.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/detail/tutorial-group-management-detail.component.ts @@ -1,4 +1,5 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { TutorialGroupDetailComponent } from 'app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { onError } from 'app/shared/util/global.utils'; import { Subject, combineLatest, finalize, switchMap, take } from 'rxjs'; @@ -8,13 +9,22 @@ import { ActivatedRoute, Router } from '@angular/router'; import { HttpErrorResponse } from '@angular/common/http'; import { Course } from 'app/entities/course.model'; import { takeUntil } from 'rxjs/operators'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { TutorialGroupRowButtonsComponent } from '../tutorial-groups-management/tutorial-group-row-buttons/tutorial-group-row-buttons.component'; @Component({ selector: 'jhi-tutorial-group-management-detail', templateUrl: './tutorial-group-management-detail.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [LoadingIndicatorContainerComponent, TutorialGroupDetailComponent, TutorialGroupRowButtonsComponent], }) export class TutorialGroupManagementDetailComponent implements OnInit, OnDestroy { + private activatedRoute = inject(ActivatedRoute); + private router = inject(Router); + private tutorialGroupService = inject(TutorialGroupsService); + private alertService = inject(AlertService); + private cdr = inject(ChangeDetectorRef); + ngUnsubscribe = new Subject(); isLoading = false; @@ -23,14 +33,6 @@ export class TutorialGroupManagementDetailComponent implements OnInit, OnDestroy tutorialGroupId: number; isAtLeastInstructor = false; - constructor( - private activatedRoute: ActivatedRoute, - private router: Router, - private tutorialGroupService: TutorialGroupsService, - private alertService: AlertService, - private cdr: ChangeDetectorRef, - ) {} - ngOnInit(): void { this.isLoading = true; combineLatest([this.activatedRoute.paramMap, this.activatedRoute.data]) diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-group-row-buttons/tutorial-group-row-buttons.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-group-row-buttons/tutorial-group-row-buttons.component.ts index 143e1782d996..49c1724b10b5 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-group-row-buttons/tutorial-group-row-buttons.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-group-row-buttons/tutorial-group-row-buttons.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output, inject } from '@angular/core'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; import { faCalendarAlt, faTrash, faUsers, faWrench } from '@fortawesome/free-solid-svg-icons'; import { EMPTY, Subject, from } from 'rxjs'; @@ -9,13 +9,21 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { RegisteredStudentsComponent } from 'app/course/tutorial-groups/tutorial-groups-management/registered-students/registered-students.component'; import { TutorialGroupSessionsManagementComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-group-sessions/tutorial-group-sessions-management/tutorial-group-sessions-management.component'; import { catchError, takeUntil } from 'rxjs/operators'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { RouterLink } from '@angular/router'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; @Component({ selector: 'jhi-tutorial-group-row-buttons', templateUrl: './tutorial-group-row-buttons.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [FaIconComponent, TranslateDirective, RouterLink, DeleteButtonDirective], }) export class TutorialGroupRowButtonsComponent implements OnDestroy { + private tutorialGroupsService = inject(TutorialGroupsService); + private modalService = inject(NgbModal); + ngUnsubscribe = new Subject(); @Input() isAtLeastInstructor = false; @@ -34,10 +42,7 @@ export class TutorialGroupRowButtonsComponent implements OnDestroy { faTrash = faTrash; faCalendar = faCalendarAlt; - public constructor( - private tutorialGroupsService: TutorialGroupsService, - private modalService: NgbModal, - ) {} + public constructor() {} openSessionDialog(event: MouseEvent) { event.stopPropagation(); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-course-information/tutorial-groups-course-information.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-course-information/tutorial-groups-course-information.component.ts index 18bb6955bd0d..7f9738d0b1ee 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-course-information/tutorial-groups-course-information.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-course-information/tutorial-groups-course-information.component.ts @@ -1,10 +1,14 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; +import { SidePanelComponent } from 'app/shared/side-panel/side-panel.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutorial-groups-course-information', templateUrl: './tutorial-groups-course-information.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [SidePanelComponent, TranslateDirective, ArtemisTranslatePipe], }) export class TutorialGroupsCourseInformationComponent { @Input() diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-export-button.component/tutorial-groups-export-button.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-export-button.component/tutorial-groups-export-button.component.ts index e8e5c16d4449..98413ced01f0 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-export-button.component/tutorial-groups-export-button.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-export-button.component/tutorial-groups-export-button.component.ts @@ -1,16 +1,23 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output, TemplateRef, ViewChild } from '@angular/core'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output, TemplateRef, ViewChild, inject } from '@angular/core'; +import { NgbDropdownButtonItem, NgbDropdownItem, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { EMPTY, Subject, from } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; import { TutorialGroupsService } from 'app/course/tutorial-groups/services/tutorial-groups.service'; import { AlertService } from 'app/core/util/alert.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; @Component({ selector: 'jhi-tutorial-groups-export-button', templateUrl: './tutorial-groups-export-button.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [NgbDropdownButtonItem, NgbDropdownItem, TranslateDirective, FormsModule], }) export class TutorialGroupsExportButtonComponent implements OnDestroy { + private modalService = inject(NgbModal); + private tutorialGroupsService = inject(TutorialGroupsService); + private alertService = inject(AlertService); + ngUnsubscribe = new Subject(); @ViewChild('exportDialog') exportDialogRef: TemplateRef; @@ -37,12 +44,6 @@ export class TutorialGroupsExportButtonComponent implements OnDestroy { { value: 'Students', selected: false }, ]; - constructor( - private modalService: NgbModal, - private tutorialGroupsService: TutorialGroupsService, - private alertService: AlertService, - ) {} - openExportDialog(event: MouseEvent) { event.stopPropagation(); const modalRef: NgbModalRef = this.modalService.open(this.exportDialogRef, { diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-button/tutorial-groups-import-button.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-button/tutorial-groups-import-button.component.ts index c26904bcfccd..72d5cc30d44c 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-button/tutorial-groups-import-button.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-button/tutorial-groups-import-button.component.ts @@ -1,15 +1,19 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output, TemplateRef, ViewChild } from '@angular/core'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, Output, TemplateRef, ViewChild, inject } from '@angular/core'; +import { NgbDropdownButtonItem, NgbDropdownItem, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { TutorialGroupsRegistrationImportDialogComponent } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-dialog/tutorial-groups-registration-import-dialog.component'; import { EMPTY, Subject, from } from 'rxjs'; import { catchError, takeUntil } from 'rxjs/operators'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-tutorial-groups-import-button', templateUrl: './tutorial-groups-import-button.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [NgbDropdownButtonItem, NgbDropdownItem, TranslateDirective], }) export class TutorialGroupsImportButtonComponent implements OnDestroy { + private modalService = inject(NgbModal); + ngUnsubscribe = new Subject(); @ViewChild('warning') @@ -19,8 +23,6 @@ export class TutorialGroupsImportButtonComponent implements OnDestroy { @Output() importFinished: EventEmitter = new EventEmitter(); - constructor(private modalService: NgbModal) {} - openTutorialGroupImportDialog(event: MouseEvent) { event.stopPropagation(); const modalRef: NgbModalRef = this.modalService.open(TutorialGroupsRegistrationImportDialogComponent, { diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-dialog/tutorial-groups-registration-import-dialog.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-dialog/tutorial-groups-registration-import-dialog.component.ts index 2f48df4e9970..f8aa6f625e7a 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-dialog/tutorial-groups-registration-import-dialog.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-import-dialog/tutorial-groups-registration-import-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild, inject } from '@angular/core'; import { faBan, faCheck, faCircleNotch, faFileImport, faSpinner } from '@fortawesome/free-solid-svg-icons'; import { TutorialGroupRegistrationImportDTO } from 'app/entities/tutorial-group/tutorial-group-import-dto.model'; import { cleanString } from 'app/shared/util/utils'; @@ -9,11 +9,14 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { Subject } from 'rxjs'; import { StudentDTO } from 'app/entities/student-dto.model'; import { HttpResponse } from '@angular/common/http'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { titleRegex } from 'app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/crud/tutorial-group-form/tutorial-group-form.component'; import { takeUntil } from 'rxjs/operators'; import { CsvDownloadService } from 'app/shared/util/CsvDownloadService'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; /** * Each row is a object with the structure @@ -42,8 +45,16 @@ type filterValues = 'all' | 'onlyImported' | 'onlyNotImported'; selector: 'jhi-tutorial-groups-import-dialog', templateUrl: './tutorial-groups-registration-import-dialog.component.html', styleUrls: ['./tutorial-groups-registration-import-dialog.component.scss'], + imports: [TranslateDirective, FaIconComponent, FormsModule, ReactiveFormsModule, ArtemisTranslatePipe], }) export class TutorialGroupsRegistrationImportDialogComponent implements OnInit, OnDestroy { + private fb = inject(FormBuilder); + private translateService = inject(TranslateService); + private activeModal = inject(NgbActiveModal); + private alertService = inject(AlertService); + private tutorialGroupService = inject(TutorialGroupsService); + private csvDownloadService = inject(CsvDownloadService); + ngUnsubscribe = new Subject(); @ViewChild('fileInput') fileInput: ElementRef; @@ -92,15 +103,6 @@ export class TutorialGroupsRegistrationImportDialogComponent implements OnInit, faFileImport = faFileImport; selectedFilter: filterValues = 'all'; - constructor( - private fb: FormBuilder, - private translateService: TranslateService, - private activeModal: NgbActiveModal, - private alertService: AlertService, - private tutorialGroupService: TutorialGroupsService, - private csvDownloadService: CsvDownloadService, - ) {} - ngOnDestroy(): void { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); diff --git a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-management.component.ts b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-management.component.ts index 932eecba9484..1f6c0f0c3f11 100644 --- a/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-management.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/tutorial-groups-management/tutorial-groups/tutorial-groups-management/tutorial-groups-management.component.ts @@ -1,6 +1,8 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { TutorialGroupFreeDaysOverviewComponent } from 'app/course/tutorial-groups/shared/tutorial-group-free-days-overview/tutorial-group-free-days-overview.component'; +import { TutorialGroupsTableComponent } from 'app/course/tutorial-groups/shared/tutorial-groups-table/tutorial-groups-table.component'; import { TutorialGroup } from 'app/entities/tutorial-group/tutorial-group.model'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { TutorialGroupsService } from 'app/course/tutorial-groups/services/tutorial-groups.service'; import { HttpErrorResponse } from '@angular/common/http'; import { Subject, combineLatest, finalize } from 'rxjs'; @@ -12,13 +14,46 @@ import { TutorialGroupFreePeriod } from 'app/entities/tutorial-group/tutorial-gr import { TutorialGroupsConfigurationService } from 'app/course/tutorial-groups/services/tutorial-groups-configuration.service'; import { takeUntil } from 'rxjs/operators'; import { TutorialGroupsConfiguration } from 'app/entities/tutorial-group/tutorial-groups-configuration.model'; +import { LoadingIndicatorContainerComponent } from 'app/shared/loading-indicator-container/loading-indicator-container.component'; +import { NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { TutorialGroupsImportButtonComponent } from './tutorial-groups-import-button/tutorial-groups-import-button.component'; +import { TutorialGroupsExportButtonComponent } from './tutorial-groups-export-button.component/tutorial-groups-export-button.component'; +import { TutorialGroupRowButtonsComponent } from './tutorial-group-row-buttons/tutorial-group-row-buttons.component'; +import { TutorialGroupsCourseInformationComponent } from './tutorial-groups-course-information/tutorial-groups-course-information.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-tutorial-groups-management', templateUrl: './tutorial-groups-management.component.html', changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + LoadingIndicatorContainerComponent, + NgbTooltip, + RouterLink, + FaIconComponent, + TranslateDirective, + NgbDropdown, + NgbDropdownToggle, + NgbDropdownMenu, + NgbDropdownItem, + TutorialGroupsImportButtonComponent, + TutorialGroupsExportButtonComponent, + TutorialGroupsTableComponent, + TutorialGroupRowButtonsComponent, + TutorialGroupsCourseInformationComponent, + TutorialGroupFreeDaysOverviewComponent, + ArtemisTranslatePipe, + ], }) export class TutorialGroupsManagementComponent implements OnInit, OnDestroy { + private tutorialGroupService = inject(TutorialGroupsService); + private activatedRoute = inject(ActivatedRoute); + private alertService = inject(AlertService); + private tutorialGroupsConfigurationService = inject(TutorialGroupsConfigurationService); + private cdr = inject(ChangeDetectorRef); + ngUnsubscribe = new Subject(); courseId: number; @@ -36,15 +71,6 @@ export class TutorialGroupsManagementComponent implements OnInit, OnDestroy { tutorialGroupFreeDays: TutorialGroupFreePeriod[] = []; - constructor( - private tutorialGroupService: TutorialGroupsService, - private router: Router, - private activatedRoute: ActivatedRoute, - private alertService: AlertService, - private tutorialGroupsConfigurationService: TutorialGroupsConfigurationService, - private cdr: ChangeDetectorRef, - ) {} - ngOnInit(): void { this.activatedRoute.data.pipe(takeUntil(this.ngUnsubscribe)).subscribe(({ course }) => { if (course) { diff --git a/src/main/webapp/app/detail-overview-list/components/boolean-detail/boolean-detail.component.ts b/src/main/webapp/app/detail-overview-list/components/boolean-detail/boolean-detail.component.ts index 4f4adffe1461..42c2eab9890b 100644 --- a/src/main/webapp/app/detail-overview-list/components/boolean-detail/boolean-detail.component.ts +++ b/src/main/webapp/app/detail-overview-list/components/boolean-detail/boolean-detail.component.ts @@ -1,13 +1,11 @@ import { Component, Input } from '@angular/core'; import type { BooleanDetail } from 'app/detail-overview-list/detail.model'; -import { NoDataComponent } from 'app/shared/no-data-component'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; @Component({ selector: 'jhi-boolean-detail', templateUrl: 'boolean-detail.component.html', - standalone: true, - imports: [NoDataComponent, ArtemisSharedComponentModule], + imports: [ArtemisSharedComponentModule], }) export class BooleanDetailComponent { @Input() detail: BooleanDetail; diff --git a/src/main/webapp/app/detail-overview-list/components/date-detail/date-detail.component.ts b/src/main/webapp/app/detail-overview-list/components/date-detail/date-detail.component.ts index cddb968cde85..ea6dc8a51126 100644 --- a/src/main/webapp/app/detail-overview-list/components/date-detail/date-detail.component.ts +++ b/src/main/webapp/app/detail-overview-list/components/date-detail/date-detail.component.ts @@ -6,7 +6,6 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; @Component({ selector: 'jhi-date-detail', templateUrl: 'date-detail.component.html', - standalone: true, imports: [NoDataComponent, ArtemisSharedModule], }) export class DateDetailComponent { diff --git a/src/main/webapp/app/detail-overview-list/components/link-detail/link-detail.component.ts b/src/main/webapp/app/detail-overview-list/components/link-detail/link-detail.component.ts index a0367d87c622..84c5faa0e0fd 100644 --- a/src/main/webapp/app/detail-overview-list/components/link-detail/link-detail.component.ts +++ b/src/main/webapp/app/detail-overview-list/components/link-detail/link-detail.component.ts @@ -6,7 +6,6 @@ import { RouterModule } from '@angular/router'; @Component({ selector: 'jhi-link-detail', templateUrl: 'link-detail.component.html', - standalone: true, imports: [NoDataComponent, RouterModule], }) export class LinkDetailComponent { diff --git a/src/main/webapp/app/detail-overview-list/components/programming-auxiliary-repository-buttons-detail/programming-auxiliary-repository-buttons-detail.component.ts b/src/main/webapp/app/detail-overview-list/components/programming-auxiliary-repository-buttons-detail/programming-auxiliary-repository-buttons-detail.component.ts index e44170601ac7..26cb40817d45 100644 --- a/src/main/webapp/app/detail-overview-list/components/programming-auxiliary-repository-buttons-detail/programming-auxiliary-repository-buttons-detail.component.ts +++ b/src/main/webapp/app/detail-overview-list/components/programming-auxiliary-repository-buttons-detail/programming-auxiliary-repository-buttons-detail.component.ts @@ -1,5 +1,4 @@ import { Component, Input } from '@angular/core'; -import { NoDataComponent } from 'app/shared/no-data-component'; import { RouterModule } from '@angular/router'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ArtemisProgrammingExerciseActionsModule } from 'app/exercises/programming/shared/actions/programming-exercise-actions.module'; @@ -10,8 +9,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; @Component({ selector: 'jhi-programming-auxiliary-repository-buttons-detail', templateUrl: 'programming-auxiliary-repository-buttons-detail.component.html', - standalone: true, - imports: [NoDataComponent, RouterModule, ArtemisSharedComponentModule, ArtemisProgrammingExerciseActionsModule, ArtemisSharedModule], + imports: [RouterModule, ArtemisSharedComponentModule, ArtemisProgrammingExerciseActionsModule, ArtemisSharedModule], }) export class ProgrammingAuxiliaryRepositoryButtonsDetailComponent { @Input() detail: ProgrammingAuxiliaryRepositoryButtonsDetail; diff --git a/src/main/webapp/app/detail-overview-list/components/programming-diff-report-detail/programming-diff-report-detail.component.ts b/src/main/webapp/app/detail-overview-list/components/programming-diff-report-detail/programming-diff-report-detail.component.ts index 46999aa24a27..c059cbe34571 100644 --- a/src/main/webapp/app/detail-overview-list/components/programming-diff-report-detail/programming-diff-report-detail.component.ts +++ b/src/main/webapp/app/detail-overview-list/components/programming-diff-report-detail/programming-diff-report-detail.component.ts @@ -13,7 +13,6 @@ import { GitDiffLineStatComponent } from 'app/exercises/programming/git-diff-rep @Component({ selector: 'jhi-programming-diff-report-detail', templateUrl: 'programming-diff-report-detail.component.html', - standalone: true, imports: [ArtemisSharedModule, ArtemisSharedComponentModule, GitDiffLineStatComponent], }) export class ProgrammingDiffReportDetailComponent implements OnDestroy { diff --git a/src/main/webapp/app/detail-overview-list/components/programming-repository-buttons-detail/programming-repository-buttons-detail.component.ts b/src/main/webapp/app/detail-overview-list/components/programming-repository-buttons-detail/programming-repository-buttons-detail.component.ts index 21676685fed1..58528fe13578 100644 --- a/src/main/webapp/app/detail-overview-list/components/programming-repository-buttons-detail/programming-repository-buttons-detail.component.ts +++ b/src/main/webapp/app/detail-overview-list/components/programming-repository-buttons-detail/programming-repository-buttons-detail.component.ts @@ -8,7 +8,6 @@ import { ArtemisProgrammingExerciseActionsModule } from 'app/exercises/programmi @Component({ selector: 'jhi-programming-repository-buttons-detail', templateUrl: 'programming-repository-buttons-detail.component.html', - standalone: true, imports: [NoDataComponent, RouterModule, ArtemisSharedComponentModule, ArtemisProgrammingExerciseActionsModule], }) export class ProgrammingRepositoryButtonsDetailComponent { diff --git a/src/main/webapp/app/detail-overview-list/components/programming-test-status-detail/programming-test-status-detail.component.ts b/src/main/webapp/app/detail-overview-list/components/programming-test-status-detail/programming-test-status-detail.component.ts index 89a43fb9aee3..47667bf81352 100644 --- a/src/main/webapp/app/detail-overview-list/components/programming-test-status-detail/programming-test-status-detail.component.ts +++ b/src/main/webapp/app/detail-overview-list/components/programming-test-status-detail/programming-test-status-detail.component.ts @@ -10,7 +10,6 @@ import { ProgrammingExerciseParticipationType } from 'app/entities/programming/p @Component({ selector: 'jhi-programming-test-status-detail', templateUrl: 'programming-test-status-detail.component.html', - standalone: true, imports: [RouterModule, ArtemisProgrammingExerciseActionsModule, SubmissionResultStatusModule, ArtemisProgrammingExerciseStatusModule, TranslateDirective], }) export class ProgrammingTestStatusDetailComponent { diff --git a/src/main/webapp/app/detail-overview-list/components/text-detail/text-detail.component.ts b/src/main/webapp/app/detail-overview-list/components/text-detail/text-detail.component.ts index a2e56d418816..2e285aedd7f7 100644 --- a/src/main/webapp/app/detail-overview-list/components/text-detail/text-detail.component.ts +++ b/src/main/webapp/app/detail-overview-list/components/text-detail/text-detail.component.ts @@ -5,7 +5,6 @@ import { NoDataComponent } from 'app/shared/no-data-component'; @Component({ selector: 'jhi-text-detail', templateUrl: 'text-detail.component.html', - standalone: true, imports: [NoDataComponent], }) export class TextDetailComponent { diff --git a/src/main/webapp/app/detail-overview-list/detail-overview-list.component.html b/src/main/webapp/app/detail-overview-list/detail-overview-list.component.html index ba2349c564d3..3d9de756c038 100644 --- a/src/main/webapp/app/detail-overview-list/detail-overview-list.component.html +++ b/src/main/webapp/app/detail-overview-list/detail-overview-list.component.html @@ -120,7 +120,7 @@

    {{ section [programmingExercise]="detail.data.exercise" [programmingLanguage]="detail.data.programmingLanguage" [isLocal]="detail.data.isLocal" - [checkoutSolutionRepository]="detail.data.exercise.buildConfig?.checkoutSolutionRepository" + [checkoutSolutionRepository]="detail.data.exercise.buildConfig?.checkoutSolutionRepository || false" /> } diff --git a/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts b/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts index 27eeca162243..f10239706aa0 100644 --- a/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts +++ b/src/main/webapp/app/detail-overview-list/detail-overview-list.component.ts @@ -11,6 +11,19 @@ import { UMLModel } from '@ls1intum/apollon'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { Subscription } from 'rxjs'; import { PROFILE_LOCALVC } from 'app/app.constants'; +import { DetailOverviewNavigationBarComponent } from '../shared/detail-overview-navigation-bar/detail-overview-navigation-bar.component'; +import { HelpIconComponent } from '../shared/components/help-icon.component'; +import { ProgrammingExerciseInstructionComponent } from '../exercises/programming/shared/instructions-render/programming-exercise-instruction.component'; +import { ProgrammingExerciseLifecycleComponent } from '../exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component'; +import { DecimalPipe, NgStyle, NgTemplateOutlet } from '@angular/common'; +import { StructuredGradingInstructionsAssessmentLayoutComponent } from '../assessment/structured-grading-instructions-assessment-layout/structured-grading-instructions-assessment-layout.component'; +import { TranslateDirective } from '../shared/language/translate.directive'; +import { IrisEnabledComponent } from '../iris/settings/shared/iris-enabled.component'; +import { ModelingEditorComponent } from '../exercises/modeling/shared/modeling-editor.component'; +import { ProgrammingExerciseRepositoryAndBuildPlanDetailsComponent } from '../exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component'; +import { ExerciseDetailDirective } from './exercise-detail.directive'; +import { NoDataComponent } from '../shared/no-data-component'; +import { ArtemisTranslatePipe } from '../shared/pipes/artemis-translate.pipe'; export interface DetailOverviewSection { headline: string; @@ -43,6 +56,23 @@ export enum DetailType { templateUrl: './detail-overview-list.component.html', styleUrls: ['./detail-overview-list.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + DetailOverviewNavigationBarComponent, + HelpIconComponent, + ProgrammingExerciseInstructionComponent, + ProgrammingExerciseLifecycleComponent, + NgTemplateOutlet, + StructuredGradingInstructionsAssessmentLayoutComponent, + TranslateDirective, + IrisEnabledComponent, + ModelingEditorComponent, + ProgrammingExerciseRepositoryAndBuildPlanDetailsComponent, + NgStyle, + ExerciseDetailDirective, + NoDataComponent, + DecimalPipe, + ArtemisTranslatePipe, + ], }) export class DetailOverviewListComponent implements OnInit, OnDestroy { protected readonly isEmpty = isEmpty; diff --git a/src/main/webapp/app/detail-overview-list/detail.module.ts b/src/main/webapp/app/detail-overview-list/detail.module.ts index c661ae788647..cbc9acfd2aa7 100644 --- a/src/main/webapp/app/detail-overview-list/detail.module.ts +++ b/src/main/webapp/app/detail-overview-list/detail.module.ts @@ -33,8 +33,8 @@ import { NoDataComponent } from 'app/shared/no-data-component'; ProgrammingExerciseRepositoryAndBuildPlanDetailsComponent, ExerciseDetailDirective, NoDataComponent, + DetailOverviewListComponent, ], - declarations: [DetailOverviewListComponent], exports: [DetailOverviewListComponent], }) export class DetailModule {} diff --git a/src/main/webapp/app/detail-overview-list/exercise-detail.directive.ts b/src/main/webapp/app/detail-overview-list/exercise-detail.directive.ts index 2bacb55c076b..f26902a73590 100644 --- a/src/main/webapp/app/detail-overview-list/exercise-detail.directive.ts +++ b/src/main/webapp/app/detail-overview-list/exercise-detail.directive.ts @@ -1,4 +1,4 @@ -import { ComponentRef, Directive, Input, OnDestroy, OnInit, Type, ViewContainerRef } from '@angular/core'; +import { ComponentRef, Directive, Input, OnDestroy, OnInit, Type, ViewContainerRef, inject } from '@angular/core'; import type { Detail, ShownDetail } from 'app/detail-overview-list/detail.model'; import { DetailType } from 'app/detail-overview-list/detail-overview-list.component'; import { TextDetailComponent } from 'app/detail-overview-list/components/text-detail/text-detail.component'; @@ -12,15 +12,14 @@ import { ProgrammingDiffReportDetailComponent } from 'app/detail-overview-list/c @Directive({ selector: '[jhiExerciseDetail]', - standalone: true, }) export class ExerciseDetailDirective implements OnInit, OnDestroy { + viewContainerRef = inject(ViewContainerRef); + @Input() detail: Detail; private componentRef: ComponentRef; - constructor(public viewContainerRef: ViewContainerRef) {} - ngOnInit() { if (!this.isShownDetail()) { return; diff --git a/src/main/webapp/app/entities/bonus.model.ts b/src/main/webapp/app/entities/bonus.model.ts index b40d156545ca..ea7b1cd4a55a 100644 --- a/src/main/webapp/app/entities/bonus.model.ts +++ b/src/main/webapp/app/entities/bonus.model.ts @@ -8,8 +8,6 @@ export class Bonus implements BaseEntity { public weight?: number; public sourceGradingScale?: GradingScale; public bonusToGradingScale?: GradingScale; - - constructor() {} } export enum BonusStrategy { @@ -41,6 +39,4 @@ export class BonusResult { public mostSeverePlagiarismVerdict?: PlagiarismVerdict; public achievedPresentationScore?: number; public presentationScoreThreshold?: number; - - constructor() {} } diff --git a/src/main/webapp/app/entities/competency.model.ts b/src/main/webapp/app/entities/competency.model.ts index c130b3a24a67..f6fa260f7876 100644 --- a/src/main/webapp/app/entities/competency.model.ts +++ b/src/main/webapp/app/entities/competency.model.ts @@ -188,8 +188,6 @@ export class CompetencyProgress { public progress?: number; public confidence?: number; public confidenceReason?: ConfidenceReason; - - constructor() {} } export class CourseCompetencyProgress { @@ -197,8 +195,6 @@ export class CourseCompetencyProgress { numberOfStudents?: number; numberOfMasteredStudents?: number; averageStudentScore?: number; - - constructor() {} } export class CompetencyRelation implements BaseEntity { @@ -242,8 +238,6 @@ export function dtoToCompetencyRelation(competencyRelationDTO: CompetencyRelatio export class CompetencyWithTailRelationDTO { competency?: CourseCompetency; tailRelations?: CompetencyRelationDTO[]; - - constructor() {} } export function getIcon(competencyTaxonomy?: CompetencyTaxonomy): IconProp { diff --git a/src/main/webapp/app/entities/competency/learning-path.model.ts b/src/main/webapp/app/entities/competency/learning-path.model.ts index 576afd4e75a5..e431d8c399eb 100644 --- a/src/main/webapp/app/entities/competency/learning-path.model.ts +++ b/src/main/webapp/app/entities/competency/learning-path.model.ts @@ -11,8 +11,6 @@ export class LearningPath implements BaseEntity { public user?: User; public course?: Course; public competencies?: CourseCompetency[]; - - constructor() {} } export class LearningPathInformationDTO { diff --git a/src/main/webapp/app/entities/complaint-response.model.ts b/src/main/webapp/app/entities/complaint-response.model.ts index 0dca177c0b1f..a2af4ad1d28d 100644 --- a/src/main/webapp/app/entities/complaint-response.model.ts +++ b/src/main/webapp/app/entities/complaint-response.model.ts @@ -14,6 +14,4 @@ export class ComplaintResponse implements BaseEntity { public isCurrentlyLocked?: boolean; // transient property that will be calculated on the server public lockEndDate?: dayjs.Dayjs; - - constructor() {} } diff --git a/src/main/webapp/app/entities/complaint.model.ts b/src/main/webapp/app/entities/complaint.model.ts index e8f4f79a7d18..2d21c7092ed2 100644 --- a/src/main/webapp/app/entities/complaint.model.ts +++ b/src/main/webapp/app/entities/complaint.model.ts @@ -21,6 +21,4 @@ export class Complaint implements BaseEntity { public team?: Team; public complaintType?: ComplaintType; public complaintResponse?: ComplaintResponse; - - constructor() {} } diff --git a/src/main/webapp/app/entities/course.model.ts b/src/main/webapp/app/entities/course.model.ts index f3084853431a..db224001c721 100644 --- a/src/main/webapp/app/entities/course.model.ts +++ b/src/main/webapp/app/entities/course.model.ts @@ -169,8 +169,6 @@ export class CourseForImportDTO { title?: string; shortName?: string; semester?: string; - - constructor() {} } export const enum CourseGroup { diff --git a/src/main/webapp/app/entities/example-submission.model.ts b/src/main/webapp/app/entities/example-submission.model.ts index 75bea8e597bb..d0f05067f8b4 100644 --- a/src/main/webapp/app/entities/example-submission.model.ts +++ b/src/main/webapp/app/entities/example-submission.model.ts @@ -11,8 +11,6 @@ export class ExampleSubmission implements BaseEntity { public submission?: Submission; public tutorParticipations?: TutorParticipation[]; public assessmentExplanation?: string; - - constructor() {} } export enum ExampleSubmissionMode { diff --git a/src/main/webapp/app/entities/lecture.model.ts b/src/main/webapp/app/entities/lecture.model.ts index d213ed53de1d..cfe447f755e9 100644 --- a/src/main/webapp/app/entities/lecture.model.ts +++ b/src/main/webapp/app/entities/lecture.model.ts @@ -23,6 +23,4 @@ export class Lecture implements BaseEntity { isAtLeastEditor?: boolean; isAtLeastInstructor?: boolean; ingested?: IngestionState; - - constructor() {} } diff --git a/src/main/webapp/app/entities/metis/post.model.ts b/src/main/webapp/app/entities/metis/post.model.ts index 39950a74af0b..b203a3e4f8b0 100644 --- a/src/main/webapp/app/entities/metis/post.model.ts +++ b/src/main/webapp/app/entities/metis/post.model.ts @@ -5,7 +5,6 @@ import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/Plagiarism export class Post extends Posting { public title?: string; - public visibleForStudents?: boolean; public answers?: AnswerPost[]; public tags?: string[]; public plagiarismCase?: PlagiarismCase; @@ -16,7 +15,6 @@ export class Post extends Posting { constructor() { super(); // set default values - this.visibleForStudents = true; this.displayPriority = DisplayPriority.NONE; } } diff --git a/src/main/webapp/app/entities/participation/tutor-participation.model.ts b/src/main/webapp/app/entities/participation/tutor-participation.model.ts index 234b49451458..99ce4666014f 100644 --- a/src/main/webapp/app/entities/participation/tutor-participation.model.ts +++ b/src/main/webapp/app/entities/participation/tutor-participation.model.ts @@ -17,6 +17,4 @@ export class TutorParticipation implements BaseEntity { public assessedExercise?: Exercise; public tutor?: User; public trainedExampleSubmissions?: ExampleSubmission[]; - - constructor() {} } diff --git a/src/main/webapp/app/entities/programming-exercise-git-diff-entry.model.ts b/src/main/webapp/app/entities/programming-exercise-git-diff-entry.model.ts index c06f8c7ea50c..2ee6c1d884da 100644 --- a/src/main/webapp/app/entities/programming-exercise-git-diff-entry.model.ts +++ b/src/main/webapp/app/entities/programming-exercise-git-diff-entry.model.ts @@ -9,6 +9,4 @@ export class ProgrammingExerciseGitDiffEntry implements BaseEntity { public startLine?: number; public previousLineCount?: number; public lineCount?: number; - - constructor() {} } diff --git a/src/main/webapp/app/entities/programming-exercise-git-diff-report.model.ts b/src/main/webapp/app/entities/programming-exercise-git-diff-report.model.ts index d688911d1ccd..ecce60142a47 100644 --- a/src/main/webapp/app/entities/programming-exercise-git-diff-report.model.ts +++ b/src/main/webapp/app/entities/programming-exercise-git-diff-report.model.ts @@ -13,6 +13,4 @@ export class ProgrammingExerciseGitDiffReport implements BaseEntity { public participationIdForRightCommit?: number; public templateRepositoryCommitHash?: string; public solutionRepositoryCommitHash?: string; - - constructor() {} } diff --git a/src/main/webapp/app/entities/quiz/course-management-statistics-model.ts b/src/main/webapp/app/entities/quiz/course-management-statistics-model.ts index 2d4a583c1837..154b4aa6e8ef 100644 --- a/src/main/webapp/app/entities/quiz/course-management-statistics-model.ts +++ b/src/main/webapp/app/entities/quiz/course-management-statistics-model.ts @@ -9,6 +9,4 @@ export class CourseManagementStatisticsModel { public averageScore: number; public exerciseType: ExerciseType; public categories?: ExerciseCategory[]; - - constructor() {} } diff --git a/src/main/webapp/app/entities/quiz/quiz-statistic-counter.model.ts b/src/main/webapp/app/entities/quiz/quiz-statistic-counter.model.ts index 4b2e0baa0475..1b9d0eae7060 100644 --- a/src/main/webapp/app/entities/quiz/quiz-statistic-counter.model.ts +++ b/src/main/webapp/app/entities/quiz/quiz-statistic-counter.model.ts @@ -4,6 +4,4 @@ export class QuizStatisticCounter implements BaseEntity { public id?: number; public ratedCounter?: number; public unRatedCounter?: number; - - constructor() {} } diff --git a/src/main/webapp/app/entities/tutorial-group/tutorial-group-registration.model.ts b/src/main/webapp/app/entities/tutorial-group/tutorial-group-registration.model.ts index 0ff00c9f2b8a..393964bb8341 100644 --- a/src/main/webapp/app/entities/tutorial-group/tutorial-group-registration.model.ts +++ b/src/main/webapp/app/entities/tutorial-group/tutorial-group-registration.model.ts @@ -12,6 +12,4 @@ export class TutorialGroupRegistration implements BaseEntity { public student?: User; public tutorialGroup?: TutorialGroup; public type?: TutorialGroupRegistrationType; - - constructor() {} } diff --git a/src/main/webapp/app/exam/exam-scores/exam-score-dtos.model.ts b/src/main/webapp/app/exam/exam-scores/exam-score-dtos.model.ts index a11b03282410..5afc37f0cbcc 100644 --- a/src/main/webapp/app/exam/exam-scores/exam-score-dtos.model.ts +++ b/src/main/webapp/app/exam/exam-scores/exam-score-dtos.model.ts @@ -12,8 +12,6 @@ export class ExamScoreDTO { public hasSecondCorrectionAndStarted: boolean; public exerciseGroups: ExerciseGroup[]; public studentResults: StudentResult[]; - - constructor() {} } export class ExerciseGroup { @@ -22,8 +20,6 @@ export class ExerciseGroup { public maxPoints: number; public numberOfParticipants: number; public containedExercises: ExerciseInfo[]; - - constructor() {} } export class ExerciseInfo { @@ -32,8 +28,6 @@ export class ExerciseInfo { public maxPoints: number; public numberOfParticipants: number; public exerciseType: string; - - constructor() {} } export class StudentResult { @@ -52,8 +46,6 @@ export class StudentResult { public gradeWithBonus?: BonusResult; public exerciseGroupIdToExerciseResult: { [key: number]: ExerciseResult }; public mostSeverePlagiarismVerdict?: PlagiarismVerdict; - - constructor() {} } export class StudentExamWithGradeDTO { @@ -72,8 +64,6 @@ export class ExerciseResult { public achievedScore?: number; public achievedPoints?: number; public hasNonEmptySubmission: boolean; - - constructor() {} } export class AggregatedExamResult { @@ -164,8 +154,6 @@ export class AggregatedExamResult { public standardGradeDeviationTotalInFirstCorrection?: number; public standardGradeDeviationNonEmptyInFirstCorrection?: number; public standardGradeDeviationSubmittedAndNonEmptyInFirstCorrection?: number; - - constructor() {} } export class AggregatedExerciseGroupResult { diff --git a/src/main/webapp/app/exam/exam-scores/exam-scores-average-scores-graph.component.ts b/src/main/webapp/app/exam/exam-scores/exam-scores-average-scores-graph.component.ts index a76a05026816..822cd14cc06b 100644 --- a/src/main/webapp/app/exam/exam-scores/exam-scores-average-scores-graph.component.ts +++ b/src/main/webapp/app/exam/exam-scores/exam-scores-average-scores-graph.component.ts @@ -1,6 +1,4 @@ import { Component, OnInit, inject, input } from '@angular/core'; -import { StatisticsService } from 'app/shared/statistics-graph/statistics.service'; -import { TranslateService } from '@ngx-translate/core'; import { GraphColors } from 'app/entities/statistics.model'; import { AggregatedExerciseGroupResult } from 'app/exam/exam-scores/exam-score-dtos.model'; import { LocaleConversionService } from 'app/shared/service/locale-conversion.service'; @@ -20,14 +18,11 @@ type NameToValueMap = { [name: string]: any }; @Component({ selector: 'jhi-exam-scores-average-scores-graph', templateUrl: './exam-scores-average-scores-graph.component.html', - standalone: true, imports: [TranslateDirective, BarChartModule, ArtemisSharedCommonModule], }) export class ExamScoresAverageScoresGraphComponent implements OnInit { private navigationUtilService = inject(ArtemisNavigationUtilService); private activatedRoute = inject(ActivatedRoute); - private service = inject(StatisticsService); - private translateService = inject(TranslateService); private localeConversionService = inject(LocaleConversionService); averageScores = input.required(); diff --git a/src/main/webapp/app/exam/exam-scores/exam-scores.component.ts b/src/main/webapp/app/exam/exam-scores/exam-scores.component.ts index 97bb7fb19d39..8a92155a2700 100644 --- a/src/main/webapp/app/exam/exam-scores/exam-scores.component.ts +++ b/src/main/webapp/app/exam/exam-scores/exam-scores.component.ts @@ -75,7 +75,6 @@ export enum MedianType { templateUrl: './exam-scores.component.html', changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: ['./exam-scores.component.scss', '../../shared/chart/vertical-bar-chart.scss'], - standalone: true, imports: [RouterLink, ArtemisSharedComponentModule, ArtemisSharedCommonModule, ExamScoresAverageScoresGraphComponent, ArtemisParticipantScoresModule, ExportModule], }) export class ExamScoresComponent implements OnInit, OnDestroy { diff --git a/src/main/webapp/app/exam/exam-scores/exam-scores.route.ts b/src/main/webapp/app/exam/exam-scores/exam-scores.route.ts index c0b815477e42..ac35ee9c1370 100644 --- a/src/main/webapp/app/exam/exam-scores/exam-scores.route.ts +++ b/src/main/webapp/app/exam/exam-scores/exam-scores.route.ts @@ -1,12 +1,12 @@ import { Route, Routes } from '@angular/router'; -import { ExamScoresComponent } from 'app/exam/exam-scores/exam-scores.component'; + import { Authority } from 'app/shared/constants/authority.constants'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; export const examScoresRoute: Route[] = [ { path: ':examId/scores', - component: ExamScoresComponent, + loadComponent: () => import('app/exam/exam-scores/exam-scores.component').then((m) => m.ExamScoresComponent), }, ]; diff --git a/src/main/webapp/app/exam/manage/exam-exercise-update.service.ts b/src/main/webapp/app/exam/manage/exam-exercise-update.service.ts index 79b53329ac10..ec9530e1735e 100644 --- a/src/main/webapp/app/exam/manage/exam-exercise-update.service.ts +++ b/src/main/webapp/app/exam/manage/exam-exercise-update.service.ts @@ -14,8 +14,6 @@ export class ExamExerciseUpdateService { private examExerciseIdForNavigationSource = new BehaviorSubject(-1); currentExerciseIdForNavigation = this.examExerciseIdForNavigationSource.asObservable(); - constructor() {} - navigateToExamExercise(exerciseId: number) { this.examExerciseIdForNavigationSource.next(exerciseId); } diff --git a/src/main/webapp/app/exam/manage/exam-management-resolve.service.ts b/src/main/webapp/app/exam/manage/exam-management-resolve.service.ts index e33354d96359..525e29e024c3 100644 --- a/src/main/webapp/app/exam/manage/exam-management-resolve.service.ts +++ b/src/main/webapp/app/exam/manage/exam-management-resolve.service.ts @@ -3,7 +3,7 @@ import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ExerciseGroupService } from 'app/exam/manage/exercise-groups/exercise-group.service'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ActivatedRouteSnapshot, Resolve } from '@angular/router'; import { Observable, filter, map, of } from 'rxjs'; import { HttpResponse } from '@angular/common/http'; @@ -14,7 +14,7 @@ import { catchError } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class CourseResolve implements Resolve { - constructor(private courseManagementService: CourseManagementService) {} + private courseManagementService = inject(CourseManagementService); resolve(route: ActivatedRouteSnapshot): Observable { const courseId = route.params['courseId']; @@ -32,7 +32,7 @@ export class CourseResolve implements Resolve { @Injectable({ providedIn: 'root' }) export class ExamResolve implements Resolve { - constructor(private examManagementService: ExamManagementService) {} + private examManagementService = inject(ExamManagementService); /** * Resolves the route by extracting the examId and returns the exam with that id if it exists @@ -64,7 +64,7 @@ export class ExamResolve implements Resolve { @Injectable({ providedIn: 'root' }) export class ExerciseGroupResolve implements Resolve { - constructor(private exerciseGroupService: ExerciseGroupService) {} + private exerciseGroupService = inject(ExerciseGroupService); /** * Resolves the route by extracting the exerciseGroupId and returns the exercise group with that id if it exists @@ -87,7 +87,7 @@ export class ExerciseGroupResolve implements Resolve { @Injectable({ providedIn: 'root' }) export class StudentExamResolve implements Resolve { - constructor(private studentExamService: StudentExamService) {} + private studentExamService = inject(StudentExamService); /** * Resolves the route by extracting the studentExamId and returns the student exam with that id if it exists diff --git a/src/main/webapp/app/exam/manage/exam-management.component.ts b/src/main/webapp/app/exam/manage/exam-management.component.ts index bd55ca990c1c..0bec4752d1c2 100644 --- a/src/main/webapp/app/exam/manage/exam-management.component.ts +++ b/src/main/webapp/app/exam/manage/exam-management.component.ts @@ -1,6 +1,6 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { Subject, Subscription } from 'rxjs'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { Exam } from 'app/entities/exam/exam.model'; @@ -17,13 +17,30 @@ import { faClipboard, faEye, faFileImport, faListAlt, faPlus, faSort, faThList, import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ExamImportComponent } from 'app/exam/manage/exams/exam-import/exam-import.component'; import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { ExamStatusComponent } from './exam-status.component'; @Component({ selector: 'jhi-exam-management', templateUrl: './exam-management.component.html', styleUrls: ['./exam-management.component.scss'], + imports: [TranslateDirective, DocumentationButtonComponent, FaIconComponent, RouterLink, SortDirective, SortByDirective, ExamStatusComponent], }) export class ExamManagementComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private courseService = inject(CourseManagementService); + private examManagementService = inject(ExamManagementService); + private eventManager = inject(EventManager); + private accountService = inject(AccountService); + private alertService = inject(AlertService); + private sortService = inject(SortService); + private modalService = inject(NgbModal); + private router = inject(Router); + readonly documentationType: DocumentationType = 'Exams'; course: Course; @@ -47,17 +64,7 @@ export class ExamManagementComponent implements OnInit, OnDestroy { faClipboard = faClipboard; faThList = faThList; - constructor( - private route: ActivatedRoute, - private courseService: CourseManagementService, - private examManagementService: ExamManagementService, - private eventManager: EventManager, - private accountService: AccountService, - private alertService: AlertService, - private sortService: SortService, - private modalService: NgbModal, - private router: Router, - ) { + constructor() { this.predicate = 'id'; this.ascending = true; } diff --git a/src/main/webapp/app/exam/manage/exam-management.module.ts b/src/main/webapp/app/exam/manage/exam-management.module.ts index d3e3677195d2..1422935400c5 100644 --- a/src/main/webapp/app/exam/manage/exam-management.module.ts +++ b/src/main/webapp/app/exam/manage/exam-management.module.ts @@ -48,7 +48,7 @@ import { ArtemisExamModePickerModule } from 'app/exam/manage/exams/exam-mode-pic import { ExamImportComponent } from 'app/exam/manage/exams/exam-import/exam-import.component'; import { ArtemisHeaderExercisePageWithDetailsModule } from 'app/exercises/shared/exercise-headers/exercise-headers.module'; import { ExamExerciseImportComponent } from 'app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { BonusComponent } from 'app/grading-system/bonus/bonus.component'; import { ArtemisModePickerModule } from 'app/exercises/shared/mode-picker/mode-picker.module'; import { StudentExamTimelineComponent } from './student-exams/student-exam-timeline/student-exam-timeline.component'; @@ -99,7 +99,6 @@ const ENTITY_STATES = [...examManagementState, ...examScoresState]; ArtemisExamModePickerModule, ArtemisHeaderExercisePageWithDetailsModule, BarChartModule, - FeatureToggleModule, ArtemisModePickerModule, StudentsUploadImagesModule, TitleChannelNameModule, @@ -113,8 +112,6 @@ const ENTITY_STATES = [...examManagementState, ...examScoresState]; SafeHtmlPipe, GradeStepBoundsPipe, BonusComponent, - ], - declarations: [ ExamManagementComponent, ExamUpdateComponent, ExamDetailComponent, diff --git a/src/main/webapp/app/exam/manage/exam-management.route.ts b/src/main/webapp/app/exam/manage/exam-management.route.ts index 16f0a8e2a5ca..6b8cd746e9ae 100644 --- a/src/main/webapp/app/exam/manage/exam-management.route.ts +++ b/src/main/webapp/app/exam/manage/exam-management.route.ts @@ -1,76 +1,36 @@ import { Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { ExamManagementComponent } from 'app/exam/manage/exam-management.component'; -import { ExamUpdateComponent } from 'app/exam/manage/exams/exam-update.component'; -import { ExamDetailComponent } from 'app/exam/manage/exams/exam-detail.component'; -import { ExerciseGroupsComponent } from 'app/exam/manage/exercise-groups/exercise-groups.component'; -import { ExerciseGroupUpdateComponent } from 'app/exam/manage/exercise-groups/exercise-group-update.component'; -import { ExamStudentsComponent } from 'app/exam/manage/students/exam-students.component'; -import { StudentExamsComponent } from 'app/exam/manage/student-exams/student-exams.component'; -import { StudentExamDetailComponent } from 'app/exam/manage/student-exams/student-exam-detail.component'; -import { ExamStudentsAttendanceCheckComponent } from 'app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component'; -import { TextExerciseUpdateComponent } from 'app/exercises/text/manage/text-exercise/text-exercise-update.component'; + import { TextExerciseResolver } from 'app/exercises/text/manage/text-exercise/text-exercise.route'; -import { FileUploadExerciseUpdateComponent } from 'app/exercises/file-upload/manage/file-upload-exercise-update.component'; -import { QuizExerciseUpdateComponent } from 'app/exercises/quiz/manage/quiz-exercise-update.component'; -import { ProgrammingExerciseUpdateComponent } from 'app/exercises/programming/manage/update/programming-exercise-update.component'; + import { ProgrammingExerciseResolve } from 'app/exercises/programming/manage/programming-exercise-management-routing.module'; -import { ModelingExerciseUpdateComponent } from 'app/exercises/modeling/manage/modeling-exercise-update.component'; -import { StudentExamSummaryComponent } from 'app/exam/manage/student-exams/student-exam-summary.component'; -import { AssessmentDashboardComponent } from 'app/course/dashboards/assessment-dashboard/assessment-dashboard.component'; -import { TestRunManagementComponent } from 'app/exam/manage/test-runs/test-run-management.component'; -import { ExamParticipationComponent } from 'app/exam/participate/exam-participation.component'; + import { PendingChangesGuard } from 'app/shared/guard/pending-changes.guard'; import { Authority } from 'app/shared/constants/authority.constants'; import { ExerciseAssessmentDashboardComponent } from 'app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component'; import { ParticipationSubmissionComponent } from 'app/exercises/shared/participation-submission/participation-submission.component'; -import { FileUploadAssessmentComponent } from 'app/exercises/file-upload/assess/file-upload-assessment.component'; + import { ParticipationComponent } from 'app/exercises/shared/participation/participation.component'; import { ExerciseScoresComponent } from 'app/exercises/shared/exercise-scores/exercise-scores.component'; -import { ModelingAssessmentEditorComponent } from 'app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component'; + import { CodeEditorTutorAssessmentContainerComponent } from 'app/exercises/programming/assess/code-editor-tutor-assessment-container.component'; import { exerciseTypes } from 'app/entities/exercise.model'; -import { FileUploadExerciseDetailComponent } from 'app/exercises/file-upload/manage/file-upload-exercise-detail.component'; -import { ModelingExerciseDetailComponent } from 'app/exercises/modeling/manage/modeling-exercise-detail.component'; -import { ProgrammingExerciseDetailComponent } from 'app/exercises/programming/manage/programming-exercise-detail.component'; -import { TextExerciseDetailComponent } from 'app/exercises/text/manage/text-exercise/text-exercise-detail.component'; -import { PlagiarismInspectorComponent } from 'app/exercises/shared/plagiarism/plagiarism-inspector/plagiarism-inspector.component'; -import { GradingSystemComponent } from 'app/grading-system/grading-system.component'; -import { ExampleSubmissionsComponent } from 'app/exercises/shared/example-submission/example-submissions.component'; -import { GradingKeyOverviewComponent } from 'app/grading-system/grading-key-overview/grading-key-overview.component'; + import { ExerciseStatisticsComponent } from 'app/exercises/shared/statistics/exercise-statistics.component'; -import { ProgrammingExerciseConfigureGradingComponent } from 'app/exercises/programming/manage/grading/programming-exercise-configure-grading.component'; -import { ExampleModelingSubmissionComponent } from 'app/exercises/modeling/manage/example-modeling/example-modeling-submission.component'; -import { QuizParticipationComponent } from 'app/exercises/quiz/participate/quiz-participation.component'; -import { QuizReEvaluateComponent } from 'app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.component'; -import { QuizPointStatisticComponent } from 'app/exercises/quiz/manage/statistics/quiz-point-statistic/quiz-point-statistic.component'; -import { QuizStatisticComponent } from 'app/exercises/quiz/manage/statistics/quiz-statistic/quiz-statistic.component'; -import { MultipleChoiceQuestionStatisticComponent } from 'app/exercises/quiz/manage/statistics/multiple-choice-question-statistic/multiple-choice-question-statistic.component'; -import { DragAndDropQuestionStatisticComponent } from 'app/exercises/quiz/manage/statistics/drag-and-drop-question-statistic/drag-and-drop-question-statistic.component'; -import { ShortAnswerQuestionStatisticComponent } from 'app/exercises/quiz/manage/statistics/short-answer-question-statistic/short-answer-question-statistic.component'; + import { OrionExerciseAssessmentDashboardComponent } from 'app/orion/assessment/orion-exercise-assessment-dashboard.component'; import { OrionTutorAssessmentComponent } from 'app/orion/assessment/orion-tutor-assessment.component'; import { isOrion } from 'app/shared/orion/orion'; import { FileUploadExerciseManagementResolve } from 'app/exercises/file-upload/manage/file-upload-exercise-management-resolve.service'; import { ModelingExerciseResolver } from 'app/exercises/modeling/manage/modeling-exercise-resolver.service'; import { CourseResolve, ExamResolve, ExerciseGroupResolve, StudentExamResolve } from 'app/exam/manage/exam-management-resolve.service'; -import { BonusComponent } from 'app/grading-system/bonus/bonus.component'; -import { SuspiciousBehaviorComponent } from 'app/exam/manage/suspicious-behavior/suspicious-behavior.component'; -import { SuspiciousSessionsOverviewComponent } from 'app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component'; -import { StudentExamTimelineComponent } from 'app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component'; -import { QuizPoolComponent } from 'app/exercises/quiz/manage/quiz-pool.component'; -import { BuildPlanEditorComponent } from 'app/exercises/programming/manage/build-plan-editor.component'; -import { QuizExerciseDetailComponent } from 'app/exercises/quiz/manage/quiz-exercise-detail.component'; -import { RepositoryViewComponent } from 'app/localvc/repository-view/repository-view.component'; + import { LocalVCGuard } from 'app/localvc/localvc-guard.service'; -import { CommitHistoryComponent } from 'app/localvc/commit-history/commit-history.component'; -import { CommitDetailsViewComponent } from 'app/localvc/commit-details-view/commit-details-view.component'; -import { VcsRepositoryAccessLogViewComponent } from 'app/localvc/vcs-repository-access-log-view/vcs-repository-access-log-view.component'; export const examManagementRoute: Routes = [ { path: '', - component: ExamManagementComponent, + loadComponent: () => import('app/exam/manage/exam-management.component').then((m) => m.ExamManagementComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.examManagement.title', @@ -79,7 +39,7 @@ export const examManagementRoute: Routes = [ }, { path: 'new', - component: ExamUpdateComponent, + loadComponent: () => import('app/exam/manage/exams/exam-update.component').then((m) => m.ExamUpdateComponent), resolve: { exam: ExamResolve, course: CourseResolve, @@ -92,7 +52,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/edit', - component: ExamUpdateComponent, + loadComponent: () => import('app/exam/manage/exams/exam-update.component').then((m) => m.ExamUpdateComponent), resolve: { exam: ExamResolve, course: CourseResolve, @@ -105,7 +65,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId', - component: ExamDetailComponent, + loadComponent: () => import('app/exam/manage/exams/exam-detail.component').then((m) => m.ExamDetailComponent), resolve: { exam: ExamResolve, }, @@ -121,7 +81,7 @@ export const examManagementRoute: Routes = [ // Exam Import { path: 'import/:examId', - component: ExamUpdateComponent, + loadComponent: () => import('app/exam/manage/exams/exam-update.component').then((m) => m.ExamUpdateComponent), resolve: { exam: ExamResolve, course: CourseResolve, @@ -137,7 +97,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups', - component: ExerciseGroupsComponent, + loadComponent: () => import('app/exam/manage/exercise-groups/exercise-groups.component').then((m) => m.ExerciseGroupsComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.examManagement.title', @@ -146,7 +106,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/new', - component: ExerciseGroupUpdateComponent, + loadComponent: () => import('app/exam/manage/exercise-groups/exercise-group-update.component').then((m) => m.ExerciseGroupUpdateComponent), resolve: { exam: ExamResolve, exerciseGroup: ExerciseGroupResolve, @@ -159,7 +119,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/edit', - component: ExerciseGroupUpdateComponent, + loadComponent: () => import('app/exam/manage/exercise-groups/exercise-group-update.component').then((m) => m.ExerciseGroupUpdateComponent), resolve: { exam: ExamResolve, exerciseGroup: ExerciseGroupResolve, @@ -176,7 +136,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/students', - component: ExamStudentsComponent, + loadComponent: () => import('app/exam/manage/students/exam-students.component').then((m) => m.ExamStudentsComponent), resolve: { exam: ExamResolve, }, @@ -191,7 +151,8 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/students/verify-attendance', - component: ExamStudentsAttendanceCheckComponent, + loadComponent: () => + import('app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component').then((m) => m.ExamStudentsAttendanceCheckComponent), resolve: { exam: ExamResolve, }, @@ -203,7 +164,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/student-exams', - component: StudentExamsComponent, + loadComponent: () => import('app/exam/manage/student-exams/student-exams.component').then((m) => m.StudentExamsComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.examManagement.title', @@ -212,7 +173,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/grading-system', - component: GradingSystemComponent, + loadComponent: () => import('app/grading-system/grading-system.component').then((m) => m.GradingSystemComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.examManagement.gradingSystem', @@ -222,7 +183,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/bonus', - component: BonusComponent, + loadComponent: () => import('app/grading-system/bonus/bonus.component').then((m) => m.BonusComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.bonus.title', @@ -231,7 +192,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/suspicious-behavior', - component: SuspiciousBehaviorComponent, + loadComponent: () => import('app/exam/manage/suspicious-behavior/suspicious-behavior.component').then((m) => m.SuspiciousBehaviorComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.examManagement.suspiciousBehavior.title', @@ -240,7 +201,8 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/suspicious-behavior/suspicious-sessions', - component: SuspiciousSessionsOverviewComponent, + loadComponent: () => + import('app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component').then((m) => m.SuspiciousSessionsOverviewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.examManagement.suspiciousBehavior.suspiciousSessions.title', @@ -249,7 +211,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/test-runs', - component: TestRunManagementComponent, + loadComponent: () => import('app/exam/manage/test-runs/test-run-management.component').then((m) => m.TestRunManagementComponent), resolve: { exam: ExamResolve, }, @@ -261,7 +223,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/test-runs/assess', - component: AssessmentDashboardComponent, + loadComponent: () => import('app/course/dashboards/assessment-dashboard/assessment-dashboard.component').then((m) => m.AssessmentDashboardComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.TA], pageTitle: 'artemisApp.examManagement.assessmentDashboard', @@ -270,7 +232,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/test-runs/:studentExamId', - component: StudentExamDetailComponent, + loadComponent: () => import('app/exam/manage/student-exams/student-exam-detail.component').then((m) => m.StudentExamDetailComponent), resolve: { studentExam: StudentExamResolve, }, @@ -282,7 +244,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/student-exams/:studentExamId', - component: StudentExamDetailComponent, + loadComponent: () => import('app/exam/manage/student-exams/student-exam-detail.component').then((m) => m.StudentExamDetailComponent), resolve: { studentExam: StudentExamResolve, }, @@ -294,7 +256,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/student-exams/:studentExamId/summary', - component: StudentExamSummaryComponent, + loadComponent: () => import('app/exam/manage/student-exams/student-exam-summary.component').then((m) => m.StudentExamSummaryComponent), resolve: { studentExam: StudentExamResolve, }, @@ -306,7 +268,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/student-exams/:studentExamId/summary/exercises/:exerciseId/repository/:participationId', - component: RepositoryViewComponent, + loadComponent: () => import('app/localvc/repository-view/repository-view.component').then((m) => m.RepositoryViewComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.repository.title', @@ -315,7 +277,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/student-exams/:studentExamId/summary/exercises/:exerciseId/repository/:participationId/commit-history', - component: CommitHistoryComponent, + loadComponent: () => import('app/localvc/commit-history/commit-history.component').then((m) => m.CommitHistoryComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.repository.commitHistory.title', @@ -324,7 +286,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/student-exams/:studentExamId/summary/exercises/:exerciseId/repository/:participationId/commit-history/:commitHash', - component: CommitDetailsViewComponent, + loadComponent: () => import('app/localvc/commit-details-view/commit-details-view.component').then((m) => m.CommitDetailsViewComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.repository.commitHistory.commitDetails.title', @@ -333,7 +295,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/student-exams/:studentExamId/exam-timeline', - component: StudentExamTimelineComponent, + loadComponent: () => import('app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component').then((m) => m.StudentExamTimelineComponent), resolve: { studentExam: StudentExamResolve, }, @@ -345,7 +307,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/student-exams/:studentExamId/summary/overview/grading-key', - component: GradingKeyOverviewComponent, + loadComponent: () => import('app/grading-system/grading-key-overview/grading-key-overview.component').then((m) => m.GradingKeyOverviewComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.examManagement.title', @@ -354,7 +316,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/student-exams/:studentExamId/summary/overview/bonus-grading-key', - component: GradingKeyOverviewComponent, + loadComponent: () => import('app/grading-system/grading-key-overview/grading-key-overview.component').then((m) => m.GradingKeyOverviewComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.examManagement.title', @@ -364,7 +326,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/test-runs/:testRunId/conduction', - component: ExamParticipationComponent, + loadComponent: () => import('app/exam/participate/exam-participation.component').then((m) => m.ExamParticipationComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.exam.title', @@ -374,7 +336,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/test-runs/:studentExamId/summary', - component: StudentExamSummaryComponent, + loadComponent: () => import('app/exam/manage/student-exams/student-exam-summary.component').then((m) => m.StudentExamSummaryComponent), resolve: { studentExam: StudentExamResolve, }, @@ -387,7 +349,7 @@ export const examManagementRoute: Routes = [ // Create Modeling Exercise { path: ':examId/exercise-groups/:exerciseGroupId/modeling-exercises/new', - component: ModelingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/modeling/manage/modeling-exercise-update.component').then((m) => m.ModelingExerciseUpdateComponent), resolve: { modelingExercise: ModelingExerciseResolver, }, @@ -400,7 +362,7 @@ export const examManagementRoute: Routes = [ // Import Modeling Exercise { path: ':examId/exercise-groups/:exerciseGroupId/modeling-exercises/import/:exerciseId', - component: ModelingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/modeling/manage/modeling-exercise-update.component').then((m) => m.ModelingExerciseUpdateComponent), resolve: { modelingExercise: ModelingExerciseResolver, }, @@ -413,7 +375,7 @@ export const examManagementRoute: Routes = [ // Edit Modeling Exercise { path: ':examId/exercise-groups/:exerciseGroupId/modeling-exercises/:exerciseId/edit', - component: ModelingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/modeling/manage/modeling-exercise-update.component').then((m) => m.ModelingExerciseUpdateComponent), resolve: { modelingExercise: ModelingExerciseResolver, }, @@ -426,7 +388,7 @@ export const examManagementRoute: Routes = [ // Create Text Exercise { path: ':examId/exercise-groups/:exerciseGroupId/text-exercises/new', - component: TextExerciseUpdateComponent, + loadComponent: () => import('app/exercises/text/manage/text-exercise/text-exercise-update.component').then((m) => m.TextExerciseUpdateComponent), resolve: { textExercise: TextExerciseResolver, }, @@ -439,7 +401,7 @@ export const examManagementRoute: Routes = [ // Import Text Exercise { path: ':examId/exercise-groups/:exerciseGroupId/text-exercises/import/:exerciseId', - component: TextExerciseUpdateComponent, + loadComponent: () => import('app/exercises/text/manage/text-exercise/text-exercise-update.component').then((m) => m.TextExerciseUpdateComponent), resolve: { textExercise: TextExerciseResolver, }, @@ -452,7 +414,7 @@ export const examManagementRoute: Routes = [ // Edit Text Exercise { path: ':examId/exercise-groups/:exerciseGroupId/text-exercises/:exerciseId/edit', - component: TextExerciseUpdateComponent, + loadComponent: () => import('app/exercises/text/manage/text-exercise/text-exercise-update.component').then((m) => m.TextExerciseUpdateComponent), resolve: { textExercise: TextExerciseResolver, }, @@ -465,7 +427,7 @@ export const examManagementRoute: Routes = [ // Create File Upload Exercise { path: ':examId/exercise-groups/:exerciseGroupId/file-upload-exercises/new', - component: FileUploadExerciseUpdateComponent, + loadComponent: () => import('app/exercises/file-upload/manage/file-upload-exercise-update.component').then((m) => m.FileUploadExerciseUpdateComponent), resolve: { fileUploadExercise: FileUploadExerciseManagementResolve, }, @@ -478,7 +440,7 @@ export const examManagementRoute: Routes = [ // Edit File Upload Exercise { path: ':examId/exercise-groups/:exerciseGroupId/file-upload-exercises/:exerciseId/edit', - component: FileUploadExerciseUpdateComponent, + loadComponent: () => import('app/exercises/file-upload/manage/file-upload-exercise-update.component').then((m) => m.FileUploadExerciseUpdateComponent), resolve: { fileUploadExercise: FileUploadExerciseManagementResolve, }, @@ -491,7 +453,7 @@ export const examManagementRoute: Routes = [ // Quiz Pool Configuration { path: ':examId/quiz-pool', - component: QuizPoolComponent, + loadComponent: () => import('app/exercises/quiz/manage/quiz-pool.component').then((m) => m.QuizPoolComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -501,7 +463,7 @@ export const examManagementRoute: Routes = [ // Import File Upload Exercise { path: ':examId/exercise-groups/:exerciseGroupId/file-upload-exercises/import/:exerciseId', - component: FileUploadExerciseUpdateComponent, + loadComponent: () => import('app/exercises/file-upload/manage/file-upload-exercise-update.component').then((m) => m.FileUploadExerciseUpdateComponent), resolve: { fileUploadExercise: FileUploadExerciseManagementResolve, }, @@ -514,7 +476,7 @@ export const examManagementRoute: Routes = [ // Create Quiz Exercise { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/new', - component: QuizExerciseUpdateComponent, + loadComponent: () => import('app/exercises/quiz/manage/quiz-exercise-update.component').then((m) => m.QuizExerciseUpdateComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -524,7 +486,7 @@ export const examManagementRoute: Routes = [ // Import Quiz Exercise { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/import/:exerciseId', - component: QuizExerciseUpdateComponent, + loadComponent: () => import('app/exercises/quiz/manage/quiz-exercise-update.component').then((m) => m.QuizExerciseUpdateComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -534,7 +496,7 @@ export const examManagementRoute: Routes = [ // Edit Quiz Exercise { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/:exerciseId/edit', - component: QuizExerciseUpdateComponent, + loadComponent: () => import('app/exercises/quiz/manage/quiz-exercise-update.component').then((m) => m.QuizExerciseUpdateComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -544,7 +506,7 @@ export const examManagementRoute: Routes = [ // Create Programming Exercise { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/new', - component: ProgrammingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/programming/manage/update/programming-exercise-update.component').then((m) => m.ProgrammingExerciseUpdateComponent), resolve: { programmingExercise: ProgrammingExerciseResolve, }, @@ -557,7 +519,7 @@ export const examManagementRoute: Routes = [ // Import programming exercise { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/import/:exerciseId', - component: ProgrammingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/programming/manage/update/programming-exercise-update.component').then((m) => m.ProgrammingExerciseUpdateComponent), resolve: { programmingExercise: ProgrammingExerciseResolve, }, @@ -570,7 +532,7 @@ export const examManagementRoute: Routes = [ // Import programming exercise from file { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/import-from-file', - component: ProgrammingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/programming/manage/update/programming-exercise-update.component').then((m) => m.ProgrammingExerciseUpdateComponent), resolve: { programmingExercise: ProgrammingExerciseResolve, }, @@ -583,7 +545,7 @@ export const examManagementRoute: Routes = [ // Edit Programming Exercise { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/edit', - component: ProgrammingExerciseUpdateComponent, + loadComponent: () => import('app/exercises/programming/manage/update/programming-exercise-update.component').then((m) => m.ProgrammingExerciseUpdateComponent), resolve: { programmingExercise: ProgrammingExerciseResolve, }, @@ -595,7 +557,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/text-exercises/:exerciseId', - component: TextExerciseDetailComponent, + loadComponent: () => import('app/exercises/text/manage/text-exercise/text-exercise-detail.component').then((m) => m.TextExerciseDetailComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.textExercise.home.title', @@ -604,7 +566,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/file-upload-exercises/:exerciseId', - component: FileUploadExerciseDetailComponent, + loadComponent: () => import('app/exercises/file-upload/manage/file-upload-exercise-detail.component').then((m) => m.FileUploadExerciseDetailComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.fileUploadExercise.home.title', @@ -613,7 +575,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/modeling-exercises/:exerciseId', - component: ModelingExerciseDetailComponent, + loadComponent: () => import('app/exercises/modeling/manage/modeling-exercise-detail.component').then((m) => m.ModelingExerciseDetailComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.modelingExercise.home.title', @@ -622,7 +584,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId', - component: ProgrammingExerciseDetailComponent, + loadComponent: () => import('app/exercises/programming/manage/programming-exercise-detail.component').then((m) => m.ProgrammingExerciseDetailComponent), resolve: { programmingExercise: ProgrammingExerciseResolve, }, @@ -634,7 +596,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/repository/:repositoryType', - component: RepositoryViewComponent, + loadComponent: () => import('app/localvc/repository-view/repository-view.component').then((m) => m.RepositoryViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -643,7 +605,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/repository/:repositoryType/vcs-access-log', - component: VcsRepositoryAccessLogViewComponent, + loadComponent: () => import('app/localvc/vcs-repository-access-log-view/vcs-repository-access-log-view.component').then((m) => m.VcsRepositoryAccessLogViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.repository.title', @@ -652,7 +614,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/repository/:repositoryType/repo/:repositoryId', - component: RepositoryViewComponent, + loadComponent: () => import('app/localvc/repository-view/repository-view.component').then((m) => m.RepositoryViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -661,7 +623,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/repository/:repositoryType/commit-history', - component: CommitHistoryComponent, + loadComponent: () => import('app/localvc/commit-history/commit-history.component').then((m) => m.CommitHistoryComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -670,7 +632,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/repository/:repositoryType/commit-history/:commitHash', - component: CommitDetailsViewComponent, + loadComponent: () => import('app/localvc/commit-details-view/commit-details-view.component').then((m) => m.CommitDetailsViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -679,7 +641,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/participations/:participationId/repository', - component: RepositoryViewComponent, + loadComponent: () => import('app/localvc/repository-view/repository-view.component').then((m) => m.RepositoryViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -688,7 +650,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/participations/:participationId/repository/vcs-access-log', - component: VcsRepositoryAccessLogViewComponent, + loadComponent: () => import('app/localvc/vcs-repository-access-log-view/vcs-repository-access-log-view.component').then((m) => m.VcsRepositoryAccessLogViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], pageTitle: 'artemisApp.repository.title', @@ -697,7 +659,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/participations/:participationId/repository/commit-history', - component: CommitHistoryComponent, + loadComponent: () => import('app/localvc/commit-history/commit-history.component').then((m) => m.CommitHistoryComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -706,7 +668,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/participations/:participationId/repository/commit-history/:commitHash', - component: CommitDetailsViewComponent, + loadComponent: () => import('app/localvc/commit-details-view/commit-details-view.component').then((m) => m.CommitDetailsViewComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.repository.title', @@ -715,7 +677,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/:exerciseId', - component: QuizExerciseDetailComponent, + loadComponent: () => import('app/exercises/quiz/manage/quiz-exercise-detail.component').then((m) => m.QuizExerciseDetailComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -724,7 +686,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/modeling-exercises/:exerciseId/plagiarism', - component: PlagiarismInspectorComponent, + loadComponent: () => import('app/exercises/shared/plagiarism/plagiarism-inspector/plagiarism-inspector.component').then((m) => m.PlagiarismInspectorComponent), resolve: { exercise: ModelingExerciseResolver, }, @@ -736,7 +698,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/text-exercises/:exerciseId/plagiarism', - component: PlagiarismInspectorComponent, + loadComponent: () => import('app/exercises/shared/plagiarism/plagiarism-inspector/plagiarism-inspector.component').then((m) => m.PlagiarismInspectorComponent), resolve: { exercise: TextExerciseResolver, }, @@ -748,7 +710,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/plagiarism', - component: PlagiarismInspectorComponent, + loadComponent: () => import('app/exercises/shared/plagiarism/plagiarism-inspector/plagiarism-inspector.component').then((m) => m.PlagiarismInspectorComponent), resolve: { exercise: ProgrammingExerciseResolve, }, @@ -760,7 +722,8 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/grading/:tab', - component: ProgrammingExerciseConfigureGradingComponent, + loadComponent: () => + import('app/exercises/programming/manage/grading/programming-exercise-configure-grading.component').then((m) => m.ProgrammingExerciseConfigureGradingComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.programmingExercise.home.title', @@ -769,7 +732,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/programming-exercises/:exerciseId/edit-build-plan', - component: BuildPlanEditorComponent, + loadComponent: () => import('app/exercises/programming/manage/build-plan-editor.component').then((m) => m.BuildPlanEditorComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.programmingExercise.buildPlanEditor', @@ -778,7 +741,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/:exerciseId/preview', - component: QuizParticipationComponent, + loadComponent: () => import('app/exercises/quiz/participate/quiz-participation.component').then((m) => m.QuizParticipationComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -788,7 +751,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/:exerciseId/solution', - component: QuizParticipationComponent, + loadComponent: () => import('app/exercises/quiz/participate/quiz-participation.component').then((m) => m.QuizParticipationComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -798,7 +761,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/:exerciseId/re-evaluate', - component: QuizReEvaluateComponent, + loadComponent: () => import('app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.component').then((m) => m.QuizReEvaluateComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -807,7 +770,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/:exerciseId/quiz-point-statistic', - component: QuizPointStatisticComponent, + loadComponent: () => import('app/exercises/quiz/manage/statistics/quiz-point-statistic/quiz-point-statistic.component').then((m) => m.QuizPointStatisticComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.course.home.title', @@ -816,7 +779,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/:exerciseId/quiz-statistic', - component: QuizStatisticComponent, + loadComponent: () => import('app/exercises/quiz/manage/statistics/quiz-statistic/quiz-statistic.component').then((m) => m.QuizStatisticComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.course.home.title', @@ -825,7 +788,10 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/:exerciseId/mc-question-statistic/:questionId', - component: MultipleChoiceQuestionStatisticComponent, + loadComponent: () => + import('app/exercises/quiz/manage/statistics/multiple-choice-question-statistic/multiple-choice-question-statistic.component').then( + (m) => m.MultipleChoiceQuestionStatisticComponent, + ), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.course.home.title', @@ -834,7 +800,10 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/:exerciseId/dnd-question-statistic/:questionId', - component: DragAndDropQuestionStatisticComponent, + loadComponent: () => + import('app/exercises/quiz/manage/statistics/drag-and-drop-question-statistic/drag-and-drop-question-statistic.component').then( + (m) => m.DragAndDropQuestionStatisticComponent, + ), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.course.home.title', @@ -843,7 +812,10 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/quiz-exercises/:exerciseId/sa-question-statistic/:questionId', - component: ShortAnswerQuestionStatisticComponent, + loadComponent: () => + import('app/exercises/quiz/manage/statistics/short-answer-question-statistic/short-answer-question-statistic.component').then( + (m) => m.ShortAnswerQuestionStatisticComponent, + ), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.course.home.title', @@ -852,7 +824,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/assessment-dashboard', - component: AssessmentDashboardComponent, + loadComponent: () => import('app/course/dashboards/assessment-dashboard/assessment-dashboard.component').then((m) => m.AssessmentDashboardComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.examManagement.assessmentDashboard', @@ -923,7 +895,7 @@ export const examManagementRoute: Routes = [ }), { path: ':examId/exercise-groups/:exerciseGroupId/text-exercises/:exerciseId/example-submissions', - component: ExampleSubmissionsComponent, + loadComponent: () => import('app/exercises/shared/example-submission/example-submissions.component').then((m) => m.ExampleSubmissionsComponent), resolve: { exercise: TextExerciseResolver, }, @@ -947,7 +919,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/modeling-exercises/:exerciseId/example-submissions', - component: ExampleSubmissionsComponent, + loadComponent: () => import('app/exercises/shared/example-submission/example-submissions.component').then((m) => m.ExampleSubmissionsComponent), resolve: { exercise: ModelingExerciseResolver, }, @@ -959,7 +931,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/modeling-exercises/:exerciseId/example-submissions/:exampleSubmissionId', - component: ExampleModelingSubmissionComponent, + loadComponent: () => import('app/exercises/modeling/manage/example-modeling/example-modeling-submission.component').then((m) => m.ExampleModelingSubmissionComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.exampleSubmission.home.editor', @@ -968,7 +940,8 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/modeling-exercises/:exerciseId/submissions/:submissionId/assessment', - component: ModelingAssessmentEditorComponent, + loadComponent: () => + import('app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component').then((m) => m.ModelingAssessmentEditorComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.apollonDiagram.detail.title', @@ -995,7 +968,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/file-upload-exercises/:exerciseId/submissions/:submissionId/assessment', - component: FileUploadAssessmentComponent, + loadComponent: () => import('app/exercises/file-upload/assess/file-upload-assessment.component').then((m) => m.FileUploadAssessmentComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.fileUploadExercise.home.title', @@ -1004,7 +977,7 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/file-upload-exercises/:exerciseId/submissions/:submissionId/assessments/:resultId', - component: FileUploadAssessmentComponent, + loadComponent: () => import('app/exercises/file-upload/assess/file-upload-assessment.component').then((m) => m.FileUploadAssessmentComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA], pageTitle: 'artemisApp.fileUploadExercise.home.title', @@ -1013,7 +986,8 @@ export const examManagementRoute: Routes = [ }, { path: ':examId/exercise-groups/:exerciseGroupId/modeling-exercises/:exerciseId/submissions/:submissionId/assessments/:resultId', - component: ModelingAssessmentEditorComponent, + loadComponent: () => + import('app/exercises/modeling/assess/modeling-assessment-editor/modeling-assessment-editor.component').then((m) => m.ModelingAssessmentEditorComponent), data: { authorities: [Authority.ADMIN, Authority.INSTRUCTOR], usePathForBreadcrumbs: true, diff --git a/src/main/webapp/app/exam/manage/exam-management.service.ts b/src/main/webapp/app/exam/manage/exam-management.service.ts index 24425d3a530e..3617e77917db 100644 --- a/src/main/webapp/app/exam/manage/exam-management.service.ts +++ b/src/main/webapp/app/exam/manage/exam-management.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ExamUserDTO } from 'app/entities/exam/exam-user-dto.model'; import { ExamUserAttendanceCheckDTO } from 'app/entities/exam/exam-users-attendance-check-dto.model'; import { filter, map, tap } from 'rxjs/operators'; @@ -28,15 +28,13 @@ type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class ExamManagementService { + private http = inject(HttpClient); + private accountService = inject(AccountService); + private entityTitleService = inject(EntityTitleService); + public resourceUrl = 'api/courses'; public adminResourceUrl = 'api/admin/courses'; - constructor( - private http: HttpClient, - private accountService: AccountService, - private entityTitleService: EntityTitleService, - ) {} - /** * Create an exam on the server using a POST request. * @param courseId The course id. diff --git a/src/main/webapp/app/exam/manage/exam-status.component.ts b/src/main/webapp/app/exam/manage/exam-status.component.ts index fafa2f201137..3becf156aa5a 100644 --- a/src/main/webapp/app/exam/manage/exam-status.component.ts +++ b/src/main/webapp/app/exam/manage/exam-status.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnDestroy, OnInit, inject } from '@angular/core'; import { faArrowRight, faCheckCircle, faCircleExclamation, faDotCircle, faTimes, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; import { Exam } from 'app/entities/exam/exam.model'; import { ExamChecklistService } from 'app/exam/manage/exams/exam-checklist-component/exam-checklist.service'; @@ -7,6 +7,12 @@ import dayjs from 'dayjs/esm'; import { round } from 'app/shared/util/utils'; import { Course } from 'app/entities/course.model'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; +import { NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; export enum ExamReviewState { UNSET = 'unset', @@ -26,8 +32,12 @@ export enum ExamConductionState { selector: 'jhi-exam-status', templateUrl: './exam-status.component.html', styleUrls: ['./exam-status.component.scss'], + imports: [NgClass, FaIconComponent, TranslateDirective, ArtemisDatePipe, ArtemisTranslatePipe, ArtemisDurationFromSecondsPipe], }) export class ExamStatusComponent implements OnChanges, OnInit, OnDestroy { + private examChecklistService = inject(ExamChecklistService); + private websocketService = inject(JhiWebsocketService); + @Input() public exam: Exam; @Input() @@ -68,11 +78,6 @@ export class ExamStatusComponent implements OnChanges, OnInit, OnDestroy { faDotCircle = faDotCircle; faCircleExclamation = faCircleExclamation; - constructor( - private examChecklistService: ExamChecklistService, - private websocketService: JhiWebsocketService, - ) {} - ngOnInit() { const submittedTopic = this.examChecklistService.getSubmittedTopic(this.exam); this.websocketService.subscribe(submittedTopic); @@ -82,7 +87,7 @@ export class ExamStatusComponent implements OnChanges, OnInit, OnDestroy { this.websocketService.receive(startedTopic).subscribe(() => (this.numberOfStarted += 1)); } - ngOnChanges(): void { + ngOnChanges() { this.examChecklistService.getExamStatistics(this.exam).subscribe((examStats) => { this.examChecklist = examStats; this.numberOfGeneratedStudentExams = this.examChecklist.numberOfGeneratedStudentExams ?? 0; diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts index 826a85b0a83d..8433bcbc1f59 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component.ts @@ -1,5 +1,5 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { faBullhorn } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { Subscription, from } from 'rxjs'; @@ -7,12 +7,18 @@ import { Subscription, from } from 'rxjs'; import { Exam } from 'app/entities/exam/exam.model'; import { AlertService } from 'app/core/util/alert.service'; import { ExamLiveAnnouncementCreateModalComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-exam-live-announcement-create-button', templateUrl: './exam-live-announcement-create-button.component.html', + imports: [FaIconComponent, TranslateDirective], }) export class ExamLiveAnnouncementCreateButtonComponent implements OnInit, OnDestroy { + private modalService = inject(NgbModal); + alertService = inject(AlertService); + @Input() exam: Exam; faBullhorn = faBullhorn; @@ -22,11 +28,6 @@ export class ExamLiveAnnouncementCreateButtonComponent implements OnInit, OnDest private timeoutRef: any; private subscription: Subscription; - constructor( - private modalService: NgbModal, - public alertService: AlertService, - ) {} - ngOnInit() { this.checkAnnouncementCreationAllowed(); } diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component.ts index 371492f0bcfc..55f5b643f701 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component.ts @@ -1,10 +1,11 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { SafeHtml } from '@angular/platform-browser'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ExamLiveEventType, ExamWideAnnouncementEvent } from 'app/exam/participate/exam-participation-live-events.service'; import { faCheckCircle, faSpinner } from '@fortawesome/free-solid-svg-icons'; import { AccountService } from 'app/core/auth/account.service'; +import { ExamLiveEventComponent } from 'app/exam/shared/events/exam-live-event.component'; import dayjs from 'dayjs/esm'; import { BoldAction } from 'app/shared/monaco-editor/model/actions/bold.action'; import { ItalicAction } from 'app/shared/monaco-editor/model/actions/italic.action'; @@ -12,13 +13,22 @@ import { UnderlineAction } from 'app/shared/monaco-editor/model/actions/underlin import { CodeAction } from 'app/shared/monaco-editor/model/actions/code.action'; import { CodeBlockAction } from 'app/shared/monaco-editor/model/actions/code-block.action'; import { OrderedListAction } from 'app/shared/monaco-editor/model/actions/ordered-list.action'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-exam-live-announcement-create-modal', templateUrl: './exam-live-announcement-create-modal.component.html', styleUrls: ['./exam-live-announcement-create-modal.component.scss'], + imports: [FormsModule, TranslateDirective, MarkdownEditorMonacoComponent, ExamLiveEventComponent, FaIconComponent], }) export class ExamLiveAnnouncementCreateModalComponent { + private activeModal = inject(NgbActiveModal); + private examManagementService = inject(ExamManagementService); + private accountService = inject(AccountService); + actions = [new BoldAction(), new ItalicAction(), new UnderlineAction(), new CodeAction(), new CodeBlockAction(), new OrderedListAction(), new OrderedListAction()]; courseId: number; @@ -35,12 +45,6 @@ export class ExamLiveAnnouncementCreateModalComponent { faSpinner = faSpinner; faCheckCircle = faCheckCircle; - constructor( - private activeModal: NgbActiveModal, - private examManagementService: ExamManagementService, - private accountService: AccountService, - ) {} - submitAnnouncement() { this.status = 'submitting'; this.examManagementService.createAnnouncement(this.courseId, this.examId, this.textContent).subscribe({ diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist-exercisegroup-table/exam-checklist-exercisegroup-table.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist-exercisegroup-table/exam-checklist-exercisegroup-table.component.ts index fcbd6ec73144..5b5bf049f4e6 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist-exercisegroup-table/exam-checklist-exercisegroup-table.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist-exercisegroup-table/exam-checklist-exercisegroup-table.component.ts @@ -3,11 +3,17 @@ import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ExerciseType, getIcon, getIconTooltip } from 'app/entities/exercise.model'; import { ExerciseGroupVariantColumn } from 'app/entities/exercise-group-variant-column.model'; import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NoDataComponent } from 'app/shared/no-data-component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exam-checklist-exercisegroup-table', templateUrl: './exam-checklist-exercisegroup-table.component.html', styleUrls: ['./exam-checklist-exercisegroup-table.component.scss'], + imports: [TranslateDirective, NgbTooltip, FaIconComponent, NoDataComponent, ArtemisTranslatePipe], }) export class ExamChecklistExerciseGroupTableComponent implements OnChanges { @Input() quizExamMaxPoints: number; diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.component.ts index f873b396b058..88e6412574a4 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnChanges, OnDestroy, OnInit, inject } from '@angular/core'; import { Exam } from 'app/entities/exam/exam.model'; import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; import { faChartBar, faEye, faListAlt, faThList, faUser, faWrench } from '@fortawesome/free-solid-svg-icons'; @@ -11,12 +11,40 @@ import dayjs from 'dayjs/esm'; import { StudentExamService } from 'app/exam/manage/student-exams/student-exam.service'; import { Subject, Subscription } from 'rxjs'; import { captureException } from '@sentry/angular'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ChecklistCheckComponent } from 'app/shared/components/checklist-check/checklist-check.component'; +import { ExamChecklistExerciseGroupTableComponent } from './exam-checklist-exercisegroup-table/exam-checklist-exercisegroup-table.component'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ProgressBarComponent } from 'app/shared/dashboards/tutor-participation-graph/progress-bar/progress-bar.component'; +import { ExamEditWorkingTimeComponent } from './exam-edit-workingtime-dialog/exam-edit-working-time.component'; +import { ExamLiveAnnouncementCreateButtonComponent } from './exam-announcement-dialog/exam-live-announcement-create-button.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exam-checklist', templateUrl: './exam-checklist.component.html', + imports: [ + TranslateDirective, + ChecklistCheckComponent, + ExamChecklistExerciseGroupTableComponent, + RouterLink, + FaIconComponent, + ProgressBarComponent, + ExamEditWorkingTimeComponent, + ExamLiveAnnouncementCreateButtonComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + ], }) export class ExamChecklistComponent implements OnChanges, OnInit, OnDestroy { + private examChecklistService = inject(ExamChecklistService); + private websocketService = inject(JhiWebsocketService); + private examManagementService = inject(ExamManagementService); + private alertService = inject(AlertService); + private studentExamService = inject(StudentExamService); + @Input() exam: Exam; @Input() getExamRoutesByIdentifier: any; private longestWorkingTimeSub: Subscription | null = null; @@ -54,14 +82,6 @@ export class ExamChecklistComponent implements OnChanges, OnInit, OnDestroy { private dialogErrorSource = new Subject(); dialogError$ = this.dialogErrorSource.asObservable(); - constructor( - private examChecklistService: ExamChecklistService, - private websocketService: JhiWebsocketService, - private examManagementService: ExamManagementService, - private alertService: AlertService, - private studentExamService: StudentExamService, - ) {} - ngOnInit() { const submittedTopic = this.examChecklistService.getSubmittedTopic(this.exam); this.websocketService.subscribe(submittedTopic); diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.service.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.service.ts index ac6133a43d76..b71d5b8d3c58 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.service.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-checklist.service.ts @@ -1,5 +1,5 @@ import { HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ExamChecklist } from 'app/entities/exam/exam-checklist.model'; import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; @@ -8,7 +8,7 @@ import { filter, map } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class ExamChecklistService { - constructor(private examService: ExamManagementService) {} + private examService = inject(ExamManagementService); /** * indicates whether all student exams are generated diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time-dialog.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time-dialog.component.ts index a1bb4e17c565..2c010341ab59 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time-dialog.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time-dialog.component.ts @@ -1,17 +1,27 @@ import { HttpResponse } from '@angular/common/http'; -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { faBan, faCheck, faSpinner } from '@fortawesome/free-solid-svg-icons'; import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { examWorkingTime } from 'app/exam/participate/exam.utils'; +import { FormsModule } from '@angular/forms'; +import { WorkingTimeChangeComponent } from 'app/exam/shared/working-time-change/working-time-change.component'; +import { WorkingTimeControlComponent } from 'app/exam/shared/working-time-control/working-time-control.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ConfirmEntityNameComponent } from 'app/shared/confirm-entity-name/confirm-entity-name.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-edit-working-time-dialog', templateUrl: './exam-edit-working-time-dialog.component.html', + imports: [FormsModule, TranslateDirective, WorkingTimeControlComponent, WorkingTimeChangeComponent, ConfirmEntityNameComponent, FaIconComponent], }) export class ExamEditWorkingTimeDialogComponent { + private activeModal = inject(NgbActiveModal); + private examManagementService = inject(ExamManagementService); + @Input() exam: Exam; @Output() examChange = new EventEmitter(); @@ -32,11 +42,6 @@ export class ExamEditWorkingTimeDialogComponent { return this.oldWorkingTime ? this.oldWorkingTime + this.workingTimeSeconds : undefined; } - constructor( - private activeModal: NgbActiveModal, - private examManagementService: ExamManagementService, - ) {} - clear(): void { this.activeModal.close(); } diff --git a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time.component.ts b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time.component.ts index 4024634666c9..fa54b2b2c421 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time.component.ts @@ -1,5 +1,5 @@ import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, inject } from '@angular/core'; import { faHourglassHalf } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { Subscription, from } from 'rxjs'; @@ -7,12 +7,18 @@ import { Subscription, from } from 'rxjs'; import { Exam } from 'app/entities/exam/exam.model'; import { AlertService } from 'app/core/util/alert.service'; import { ExamEditWorkingTimeDialogComponent } from './exam-edit-working-time-dialog.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-exam-edit-working-time', templateUrl: './exam-edit-working-time.component.html', + imports: [FaIconComponent, TranslateDirective], }) export class ExamEditWorkingTimeComponent implements OnInit, OnDestroy { + private modalService = inject(NgbModal); + alertService = inject(AlertService); + @Input() exam: Exam; @Output() examChange = new EventEmitter(); @@ -23,11 +29,6 @@ export class ExamEditWorkingTimeComponent implements OnInit, OnDestroy { private timeoutRef: any; private subscription: Subscription; - constructor( - private modalService: NgbModal, - public alertService: AlertService, - ) {} - ngOnInit() { this.checkWorkingTimeChangeAllowed(); } diff --git a/src/main/webapp/app/exam/manage/exams/exam-detail.component.ts b/src/main/webapp/app/exam/manage/exams/exam-detail.component.ts index c0f4130e83c1..cbf58cbb97b2 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-detail.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-detail.component.ts @@ -1,5 +1,5 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { SafeHtml } from '@angular/platform-browser'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { Observable, Subject, map } from 'rxjs'; @@ -18,12 +18,28 @@ import { DetailOverviewSection, DetailType } from 'app/detail-overview-list/deta import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; import { scrollToTopOfPage } from 'app/shared/util/utils'; import { ExerciseType } from 'app/entities/exercise.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { CourseExamArchiveButtonComponent } from 'app/shared/components/course-exam-archive-button/course-exam-archive-button.component'; +import { ExamChecklistComponent } from './exam-checklist-component/exam-checklist.component'; +import { DetailOverviewListComponent } from 'app/detail-overview-list/detail-overview-list.component'; @Component({ selector: 'jhi-exam-detail', templateUrl: './exam-detail.component.html', + imports: [TranslateDirective, RouterLink, FaIconComponent, DeleteButtonDirective, CourseExamArchiveButtonComponent, ExamChecklistComponent, DetailOverviewListComponent], }) export class ExamDetailComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private artemisMarkdown = inject(ArtemisMarkdownService); + private accountService = inject(AccountService); + private examManagementService = inject(ExamManagementService); + private router = inject(Router); + private alertService = inject(AlertService); + private gradingSystemService = inject(GradingSystemService); + private artemisDurationFromSecondsPipe = inject(ArtemisDurationFromSecondsPipe); + exam: Exam; formattedStartText?: SafeHtml; formattedConfirmationStartText?: SafeHtml; @@ -53,17 +69,6 @@ export class ExamDetailComponent implements OnInit, OnDestroy { examDetailSections: DetailOverviewSection[]; - constructor( - private route: ActivatedRoute, - private artemisMarkdown: ArtemisMarkdownService, - private accountService: AccountService, - private examManagementService: ExamManagementService, - private router: Router, - private alertService: AlertService, - private gradingSystemService: GradingSystemService, - private artemisDurationFromSecondsPipe: ArtemisDurationFromSecondsPipe, - ) {} - /** * Initialize the exam */ diff --git a/src/main/webapp/app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component.ts b/src/main/webapp/app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component.ts index 9739343cb687..02cf547cfff6 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component.ts @@ -4,11 +4,18 @@ import { faCheckDouble, faFont } from '@fortawesome/free-solid-svg-icons'; import { Exercise, ExerciseType, getIcon } from 'app/entities/exercise.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { EXERCISE_TITLE_NAME_REGEX, SHORT_NAME_PATTERN } from 'app/shared/constants/input.constants'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { FormsModule } from '@angular/forms'; +import { NgClass } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { DifficultyBadgeComponent } from 'app/exercises/shared/exercise-headers/difficulty-badge.component'; @Component({ selector: 'jhi-exam-exercise-import', templateUrl: './exam-exercise-import.component.html', styleUrls: ['./exam-exercise-import.component.scss'], + imports: [TranslateDirective, HelpIconComponent, FormsModule, NgClass, FaIconComponent, DifficultyBadgeComponent], }) export class ExamExerciseImportComponent implements OnInit { @Input() exam: Exam; @@ -41,8 +48,6 @@ export class ExamExerciseImportComponent implements OnInit { getExerciseIcon = getIcon; - constructor() {} - ngOnInit(): void { this.initializeSelectedExercisesAndContainsProgrammingExercisesMaps(); // If the exam is imported into the same course, the title + shortName of Programming Exercises must be changed diff --git a/src/main/webapp/app/exam/manage/exams/exam-import/exam-import-paging.service.ts b/src/main/webapp/app/exam/manage/exams/exam-import/exam-import-paging.service.ts index ca9bcbf3f1fc..6ab2aabd2d7f 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-import/exam-import-paging.service.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-import/exam-import-paging.service.ts @@ -1,5 +1,5 @@ import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Exam } from 'app/entities/exam/exam.model'; import { PagingService } from 'app/exercises/shared/manage/paging.service'; import { SearchResult, SearchTermPageableSearch } from 'app/shared/table/pageable-table'; @@ -10,9 +10,11 @@ type EntityResponseType = SearchResult; @Injectable({ providedIn: 'root' }) export class ExamImportPagingService extends PagingService { + private http = inject(HttpClient); + private static readonly RESOURCE_URL = 'api/exams'; - constructor(private http: HttpClient) { + constructor() { super(); } diff --git a/src/main/webapp/app/exam/manage/exams/exam-import/exam-import.component.ts b/src/main/webapp/app/exam/manage/exams/exam-import/exam-import.component.ts index f97c8c3d537b..d813eac2dd2b 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-import/exam-import.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-import/exam-import.component.ts @@ -1,22 +1,30 @@ import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { Component, Input, ViewChild } from '@angular/core'; -import { Router } from '@angular/router'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { Component, Input, ViewChild, inject } from '@angular/core'; import { AlertService } from 'app/core/util/alert.service'; import { Exam } from 'app/entities/exam/exam.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ExamExerciseImportComponent } from 'app/exam/manage/exams/exam-exercise-import/exam-exercise-import.component'; -import { ExamImportPagingService } from 'app/exam/manage/exams/exam-import/exam-import-paging.service'; import { ImportComponent } from 'app/shared/import/import.component'; -import { SortService } from 'app/shared/service/sort.service'; import { onError } from 'app/shared/util/global.utils'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbHighlight, NgbPagination } from '@ng-bootstrap/ng-bootstrap'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { ExamImportPagingService } from 'app/exam/manage/exams/exam-import/exam-import-paging.service'; @Component({ selector: 'jhi-exam-import', templateUrl: './exam-import.component.html', + imports: [FormsModule, TranslateDirective, SortDirective, SortByDirective, FaIconComponent, NgbHighlight, ButtonComponent, NgbPagination, ExamExerciseImportComponent], }) export class ExamImportComponent extends ImportComponent { + private examManagementService = inject(ExamManagementService); + private alertService = inject(AlertService); + // boolean to indicate, if the import modal should include the exerciseGroup selection subsequently. @Input() subsequentExerciseGroupSelection: boolean; // Values to specify the target of the exercise group import @@ -30,15 +38,9 @@ export class ExamImportComponent extends ImportComponent { isImportingExercises = false; isImportInSameCourse = false; - constructor( - router: Router, - sortService: SortService, - activeModal: NgbActiveModal, - pagingService: ExamImportPagingService, - private examManagementService: ExamManagementService, - private alertService: AlertService, - ) { - super(router, sortService, activeModal, pagingService); + constructor() { + const pagingService = inject(ExamImportPagingService); + super(pagingService); } /** diff --git a/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.component.ts b/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.component.ts index c022b55ec428..d3e4a19ee370 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.component.ts @@ -1,10 +1,13 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Exam } from 'app/entities/exam/exam.model'; +import { NgClass } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-exam-mode-picker', templateUrl: './exam-mode-picker.component.html', styleUrls: ['./exam-mode-picker.component.scss'], + imports: [NgClass, TranslateDirective], }) export class ExamModePickerComponent { @Input() exam: Exam; diff --git a/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.module.ts b/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.module.ts index 4fbbcc666a8c..50ab25c83469 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.module.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-mode-picker/exam-mode-picker.module.ts @@ -4,8 +4,7 @@ import { ExamModePickerComponent } from 'app/exam/manage/exams/exam-mode-picker/ import { ArtemisSharedModule } from 'app/shared/shared.module'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [ExamModePickerComponent], + imports: [ArtemisSharedModule, ExamModePickerComponent], exports: [ExamModePickerComponent], }) export class ArtemisExamModePickerModule {} diff --git a/src/main/webapp/app/exam/manage/exams/exam-update.component.ts b/src/main/webapp/app/exam/manage/exams/exam-update.component.ts index 610798664c76..548d37ab5a33 100644 --- a/src/main/webapp/app/exam/manage/exams/exam-update.component.ts +++ b/src/main/webapp/app/exam/manage/exams/exam-update.component.ts @@ -1,10 +1,11 @@ +import { WorkingTimeChangeComponent } from 'app/exam/shared/working-time-change/working-time-change.component'; import dayjs from 'dayjs/esm'; import { omit } from 'lodash-es'; import { combineLatest, takeWhile } from 'rxjs'; import { map } from 'rxjs/operators'; -import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { Component, OnDestroy, OnInit, TemplateRef, ViewChild, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { faBan, faExclamationTriangle, faSave } from '@fortawesome/free-solid-svg-icons'; import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; @@ -18,12 +19,48 @@ import { DocumentationType } from 'app/shared/components/documentation-button/do import { ConfirmAutofocusModalComponent } from 'app/shared/components/confirm-autofocus-modal.component'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { examWorkingTime, normalWorkingTime } from 'app/exam/participate/exam.utils'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { TitleChannelNameComponent } from 'app/shared/form/title-channel-name/title-channel-name.component'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { ExamModePickerComponent } from './exam-mode-picker/exam-mode-picker.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; +import { CustomMinDirective } from 'app/shared/validators/custom-min-validator.directive'; +import { CustomMaxDirective } from 'app/shared/validators/custom-max-validator.directive'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; @Component({ selector: 'jhi-exam-update', templateUrl: './exam-update.component.html', + imports: [ + FormsModule, + TranslateDirective, + DocumentationButtonComponent, + TitleChannelNameComponent, + HelpIconComponent, + ExamModePickerComponent, + NgbTooltip, + FaIconComponent, + WorkingTimeChangeComponent, + FormDateTimePickerComponent, + CustomMinDirective, + CustomMaxDirective, + ExamExerciseImportComponent, + MarkdownEditorMonacoComponent, + ArtemisTranslatePipe, + ], }) export class ExamUpdateComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private examManagementService = inject(ExamManagementService); + private alertService = inject(AlertService); + private navigationUtilService = inject(ArtemisNavigationUtilService); + private modalService = inject(NgbModal); + private router = inject(Router); + private artemisTranslatePipe = inject(ArtemisTranslatePipe); + protected readonly faSave = faSave; protected readonly faBan = faBan; protected readonly faExclamationTriangle = faExclamationTriangle; @@ -46,16 +83,6 @@ export class ExamUpdateComponent implements OnInit, OnDestroy { @ViewChild('workingTimeConfirmationContent') public workingTimeConfirmationContent: TemplateRef; - constructor( - private route: ActivatedRoute, - private examManagementService: ExamManagementService, - private alertService: AlertService, - private navigationUtilService: ArtemisNavigationUtilService, - private modalService: NgbModal, - private router: Router, - private artemisTranslatePipe: ArtemisTranslatePipe, - ) {} - ngOnInit(): void { combineLatest([this.route.url, this.route.data]) .pipe(takeWhile(() => this.componentActive)) diff --git a/src/main/webapp/app/exam/manage/exercise-groups/exercise-group-update.component.ts b/src/main/webapp/app/exam/manage/exercise-groups/exercise-group-update.component.ts index 7acfb1fac6a6..d4a951a34a1f 100644 --- a/src/main/webapp/app/exam/manage/exercise-groups/exercise-group-update.component.ts +++ b/src/main/webapp/app/exam/manage/exercise-groups/exercise-group-update.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; @@ -8,12 +8,22 @@ import { ExerciseGroupService } from 'app/exam/manage/exercise-groups/exercise-g import { Exam } from 'app/entities/exam/exam.model'; import { onError } from 'app/shared/util/global.utils'; import { faBan, faSave } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { NgbAlert } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-exercise-group-update', templateUrl: './exercise-group-update.component.html', + imports: [TranslateDirective, FormsModule, NgbAlert, FaIconComponent], }) export class ExerciseGroupUpdateComponent implements OnInit { + private route = inject(ActivatedRoute); + private router = inject(Router); + private exerciseGroupService = inject(ExerciseGroupService); + private alertService = inject(AlertService); + readonly alertType = 'info'; courseId: number; exam: Exam; @@ -23,13 +33,6 @@ export class ExerciseGroupUpdateComponent implements OnInit { faBan = faBan; faSave = faSave; - constructor( - private route: ActivatedRoute, - private router: Router, - private exerciseGroupService: ExerciseGroupService, - private alertService: AlertService, - ) {} - /** * Initialize the courseId and exerciseGroup */ diff --git a/src/main/webapp/app/exam/manage/exercise-groups/exercise-group.service.ts b/src/main/webapp/app/exam/manage/exercise-groups/exercise-group.service.ts index 2481ccc98dbd..6d749e09796e 100644 --- a/src/main/webapp/app/exam/manage/exercise-groups/exercise-group.service.ts +++ b/src/main/webapp/app/exam/manage/exercise-groups/exercise-group.service.ts @@ -1,5 +1,4 @@ -import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; @@ -9,12 +8,9 @@ type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class ExerciseGroupService { - public resourceUrl = 'api/courses'; + private http = inject(HttpClient); - constructor( - private router: Router, - private http: HttpClient, - ) {} + public resourceUrl = 'api/courses'; /** * Create an exercise group on the server using a POST request. diff --git a/src/main/webapp/app/exam/manage/exercise-groups/exercise-groups.component.ts b/src/main/webapp/app/exam/manage/exercise-groups/exercise-groups.component.ts index 8fcac15d391b..63a43da95f72 100644 --- a/src/main/webapp/app/exam/manage/exercise-groups/exercise-groups.component.ts +++ b/src/main/webapp/app/exam/manage/exercise-groups/exercise-groups.component.ts @@ -1,5 +1,5 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { Component, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { Subject, forkJoin, of } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { ExerciseGroupService } from 'app/exam/manage/exercise-groups/exercise-group.service'; @@ -13,7 +13,6 @@ import { Course } from 'app/entities/course.model'; import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; -import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { AlertService } from 'app/core/util/alert.service'; import { EventManager } from 'app/core/util/event-manager.service'; @@ -34,14 +33,48 @@ import { ExamImportComponent } from 'app/exam/manage/exams/exam-import/exam-impo import { ExerciseImportWrapperComponent } from 'app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { PROFILE_LOCALCI, PROFILE_LOCALVC } from 'app/app.constants'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ProgrammingExerciseGroupCellComponent } from './programming-exercise-cell/programming-exercise-group-cell.component'; +import { QuizExerciseGroupCellComponent } from './quiz-exercise-cell/quiz-exercise-group-cell.component'; +import { ModelingExerciseGroupCellComponent } from './modeling-exercise-cell/modeling-exercise-group-cell.component'; +import { FileUploadExerciseGroupCellComponent } from './file-upload-exercise-cell/file-upload-exercise-group-cell.component'; +import { ExamExerciseRowButtonsComponent } from 'app/exercises/shared/exam-exercise-row-buttons/exam-exercise-row-buttons.component'; +import { LowerCasePipe } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exercise-groups', templateUrl: './exercise-groups.component.html', styleUrls: ['./exercise-groups.component.scss'], + imports: [ + TranslateDirective, + FaIconComponent, + RouterLink, + HelpIconComponent, + DeleteButtonDirective, + ProgrammingExerciseGroupCellComponent, + QuizExerciseGroupCellComponent, + ModelingExerciseGroupCellComponent, + FileUploadExerciseGroupCellComponent, + ExamExerciseRowButtonsComponent, + LowerCasePipe, + ArtemisTranslatePipe, + ], }) export class ExerciseGroupsComponent implements OnInit { - participationType = ProgrammingExerciseParticipationType; + private route = inject(ActivatedRoute); + private exerciseGroupService = inject(ExerciseGroupService); + exerciseService = inject(ExerciseService); + private examManagementService = inject(ExamManagementService); + private eventManager = inject(EventManager); + private alertService = inject(AlertService); + private modalService = inject(NgbModal); + private router = inject(Router); + private profileService = inject(ProfileService); + courseId: number; course: Course; examId: number; @@ -69,18 +102,6 @@ export class ExerciseGroupsComponent implements OnInit { faAngleDown = faAngleDown; faFileImport = faFileImport; - constructor( - private route: ActivatedRoute, - private exerciseGroupService: ExerciseGroupService, - public exerciseService: ExerciseService, - private examManagementService: ExamManagementService, - private eventManager: EventManager, - private alertService: AlertService, - private modalService: NgbModal, - private router: Router, - private profileService: ProfileService, - ) {} - /** * Initialize the courseId and examId. Get all exercise groups for the exam. Setup dictionary for exercise groups which contain programming exercises. * See {@link setupExerciseGroupToExerciseTypesDict}. diff --git a/src/main/webapp/app/exam/manage/exercise-groups/modeling-exercise-cell/modeling-exercise-group-cell.component.ts b/src/main/webapp/app/exam/manage/exercise-groups/modeling-exercise-cell/modeling-exercise-group-cell.component.ts index 53966e6022df..327e5e041320 100644 --- a/src/main/webapp/app/exam/manage/exercise-groups/modeling-exercise-cell/modeling-exercise-group-cell.component.ts +++ b/src/main/webapp/app/exam/manage/exercise-groups/modeling-exercise-cell/modeling-exercise-group-cell.component.ts @@ -1,11 +1,13 @@ import { Component, Input } from '@angular/core'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-modeling-exercise-group-cell', templateUrl: './modeling-exercise-group-cell.component.html', styles: [':host{display: contents}'], + imports: [ArtemisTranslatePipe], }) export class ModelingExerciseGroupCellComponent { exerciseType = ExerciseType; diff --git a/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.ts b/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.ts index a171fc11d30e..95128f04f992 100644 --- a/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.ts +++ b/src/main/webapp/app/exam/manage/exercise-groups/programming-exercise-cell/programming-exercise-group-cell.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseParticipationType } from 'app/entities/programming/programming-exercise-participation.model'; @@ -10,13 +10,22 @@ import { downloadZipFileFromResponse } from 'app/shared/util/download.util'; import { AlertService } from 'app/core/util/alert.service'; import { faDownload } from '@fortawesome/free-solid-svg-icons'; import { PROFILE_LOCALVC, PROFILE_THEIA } from 'app/app.constants'; +import { RouterLink } from '@angular/router'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ProgrammingExerciseInstructorStatusComponent } from 'app/exercises/programming/manage/status/programming-exercise-instructor-status.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-programming-exercise-group-cell', templateUrl: './programming-exercise-group-cell.component.html', styles: [':host{display: contents}'], + imports: [RouterLink, FaIconComponent, ProgrammingExerciseInstructorStatusComponent, TranslateDirective], }) export class ProgrammingExerciseGroupCellComponent implements OnInit { + private profileService = inject(ProfileService); + private programmingExerciseService = inject(ProgrammingExerciseService); + private alertService = inject(AlertService); + participationType = ProgrammingExerciseParticipationType; programmingExercise: ProgrammingExercise; @@ -40,12 +49,6 @@ export class ProgrammingExerciseGroupCellComponent implements OnInit { faDownload = faDownload; - constructor( - private profileService: ProfileService, - private programmingExerciseService: ProgrammingExerciseService, - private alertService: AlertService, - ) {} - ngOnInit(): void { this.profileService.getProfileInfo().subscribe((profileInfo) => { this.localVCEnabled = profileInfo.activeProfiles.includes(PROFILE_LOCALVC); diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-detail-table-row/student-exam-detail-table-row.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam-detail-table-row/student-exam-detail-table-row.component.ts index fd2f3fe264ca..8bdaacb89d04 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-detail-table-row/student-exam-detail-table-row.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-detail-table-row/student-exam-detail-table-row.component.ts @@ -7,12 +7,16 @@ import { Course } from 'app/entities/course.model'; import { Result } from 'app/entities/result.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { faFolderOpen } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { RouterLink } from '@angular/router'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ - /* eslint-disable-next-line @angular-eslint/component-selector */ selector: '[jhi-student-exam-detail-table-row]', templateUrl: './student-exam-detail-table-row.component.html', providers: [], + imports: [FaIconComponent, TranslateDirective, RouterLink, ArtemisTranslatePipe], }) export class StudentExamDetailTableRowComponent implements OnChanges { @Input() exercise: Exercise; diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-detail.component.html b/src/main/webapp/app/exam/manage/student-exams/student-exam-detail.component.html index 7b825a7471e5..790a185740b4 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-detail.component.html +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-detail.component.html @@ -70,7 +70,7 @@
    : - +
    }
    diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-detail.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam-detail.component.ts index 8d8a79acccfb..1adc225e67ce 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-detail.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-detail.component.ts @@ -1,25 +1,50 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { StudentExam } from 'app/entities/student-exam.model'; import { StudentExamService } from 'app/exam/manage/student-exams/student-exam.service'; import { Course } from 'app/entities/course.model'; import { User } from 'app/core/user/user.model'; import { AlertService } from 'app/core/util/alert.service'; +import { TestExamWorkingTimeComponent } from 'app/exam/shared/testExam-workingTime/test-exam-working-time.component'; +import { WorkingTimeControlComponent } from 'app/exam/shared/working-time-control/working-time-control.component'; import dayjs from 'dayjs/esm'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { getLatestSubmissionResult, setLatestSubmissionResult } from 'app/entities/submission.model'; import { GradeType } from 'app/entities/grading-scale.model'; import { faSave } from '@fortawesome/free-solid-svg-icons'; import { Exercise } from 'app/entities/exercise.model'; import { StudentExamWithGradeDTO } from 'app/exam/exam-scores/exam-score-dtos.model'; import { combineLatest, takeWhile } from 'rxjs'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { StudentExamDetailTableRowComponent } from './student-exam-detail-table-row/student-exam-detail-table-row.component'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-student-exam-detail', templateUrl: './student-exam-detail.component.html', styleUrls: ['./student-exam-detail.component.scss'], + imports: [ + TranslateDirective, + FormsModule, + WorkingTimeControlComponent, + FaIconComponent, + TestExamWorkingTimeComponent, + NgbTooltip, + RouterLink, + StudentExamDetailTableRowComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + ], }) export class StudentExamDetailComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private studentExamService = inject(StudentExamService); + private alertService = inject(AlertService); + private modalService = inject(NgbModal); + examId: number; courseId: number; studentExam: StudentExam; @@ -47,13 +72,6 @@ export class StudentExamDetailComponent implements OnInit, OnDestroy { private componentActive = true; - constructor( - private route: ActivatedRoute, - private studentExamService: StudentExamService, - private alertService: AlertService, - private modalService: NgbModal, - ) {} - /** * Initialize the courseId and studentExam */ diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-status/student-exam-status.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam-status/student-exam-status.component.ts index 2793d9ec48c5..1ed4390a80d3 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-status/student-exam-status.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-status/student-exam-status.component.ts @@ -1,5 +1,9 @@ import { Component, Input } from '@angular/core'; import { faCheckCircle, faExclamationTriangle, faInfoCircle } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; /** * Status indicator for student exams @@ -8,6 +12,7 @@ import { faCheckCircle, faExclamationTriangle, faInfoCircle } from '@fortawesome @Component({ selector: 'jhi-student-exam-status', templateUrl: `./student-exam-status.component.html`, + imports: [FaIconComponent, TranslateDirective, NgbTooltip, ArtemisTranslatePipe], }) export class StudentExamStatusComponent { @Input() hasStudentsWithoutExam: boolean; diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-summary.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam-summary.component.ts index f6b73dac9994..db06641c336f 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-summary.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-summary.component.ts @@ -1,15 +1,17 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { StudentExam } from 'app/entities/student-exam.model'; +import { ExamResultSummaryComponent } from '../../participate/summary/exam-result-summary.component'; @Component({ selector: 'jhi-student-exam-summary', template: '', + imports: [ExamResultSummaryComponent], }) export class StudentExamSummaryComponent implements OnInit { - studentExam: StudentExam; + private route = inject(ActivatedRoute); - constructor(private route: ActivatedRoute) {} + studentExam: StudentExam; /** * Initialize the studentExam diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts index c3979797994f..e7c8c01f3003 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component.ts @@ -1,27 +1,37 @@ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output, signal } from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, inject, signal } from '@angular/core'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { ButtonSize } from 'app/shared/components/button.component'; import { GitDiffReportModalComponent } from 'app/exercises/programming/git-diff-report/git-diff-report-modal.component'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { ProgrammingExerciseService } from 'app/exercises/programming/manage/services/programming-exercise.service'; -import { Exercise, IncludedInOverallScore } from 'app/entities/exercise.model'; +import { IncludedInOverallScore } from 'app/entities/exercise.model'; import { ExamSubmissionComponent } from 'app/exam/participate/exercises/exam-submission.component'; -import { Submission } from 'app/entities/submission.model'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; import { faCodeCompare } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; import { ExamPageComponent } from 'app/exam/participate/exercises/exam-page.component'; import { Observable, Subject, Subscription, debounceTime, take } from 'rxjs'; import { CachedRepositoryFilesService } from 'app/exercises/programming/manage/services/cached-repository-files.service'; +import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-headers/included-in-score-badge.component'; +import { CommitsInfoComponent } from 'app/exercises/programming/shared/commits-info/commits-info.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { GitDiffLineStatComponent } from 'app/exercises/programming/git-diff-report/git-diff-line-stat.component'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-programming-exam-diff', templateUrl: './programming-exercise-exam-diff.component.html', providers: [{ provide: ExamSubmissionComponent, useExisting: ProgrammingExerciseExamDiffComponent }], + imports: [IncludedInScoreBadgeComponent, CommitsInfoComponent, TranslateDirective, GitDiffLineStatComponent, NgbTooltip, ButtonComponent, ArtemisTranslatePipe], }) export class ProgrammingExerciseExamDiffComponent extends ExamPageComponent implements OnInit, OnDestroy { + private programmingExerciseService = inject(ProgrammingExerciseService); + private modalService = inject(NgbModal); + private cachedRepositoryFilesService = inject(CachedRepositoryFilesService); + @Input() exercise: ProgrammingExercise; @Input() previousSubmission: ProgrammingSubmission | undefined; @Input() currentSubmission: ProgrammingSubmission; @@ -30,6 +40,7 @@ export class ProgrammingExerciseExamDiffComponent extends ExamPageComponent impl @Input() cachedDiffReports: Map = new Map(); @Output() cachedDiffReportsChange = new EventEmitter>(); @Input() exerciseIdSubject: Subject = new Subject(); + isLoadingDiffReport: boolean; addedLineCount: number; removedLineCount: number; @@ -42,15 +53,6 @@ export class ProgrammingExerciseExamDiffComponent extends ExamPageComponent impl readonly faCodeCompare = faCodeCompare; readonly IncludedInOverallScore = IncludedInOverallScore; - constructor( - protected changeDetectorReference: ChangeDetectorRef, - private programmingExerciseService: ProgrammingExerciseService, - private modalService: NgbModal, - private cachedRepositoryFilesService: CachedRepositoryFilesService, - ) { - super(changeDetectorReference); - } - ngOnInit() { // we subscribe to the exercise id because this allows us to avoid reloading the diff report every time the user switches between submission timestamps this.exerciseIdSubscription = this.exerciseIdSubject.pipe(debounceTime(200)).subscribe(() => { @@ -132,14 +134,6 @@ export class ProgrammingExerciseExamDiffComponent extends ExamPageComponent impl }); } - getSubmission(): Submission | undefined { - return this.currentSubmission; - } - - getExercise(): Exercise { - return this.exercise; - } - private calculateMapKey() { return JSON.stringify([this.previousSubmission?.id, this.currentSubmission.id!]); } diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts index ac7f501a52d5..4b8a63f9dd8c 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam-timeline/student-exam-timeline.component.ts @@ -1,10 +1,13 @@ -import { AfterViewInit, ChangeDetectorRef, Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { StudentExam } from 'app/entities/student-exam.model'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { ExamPage } from 'app/entities/exam/exam-page.model'; import { ExamSubmissionComponent } from 'app/exam/participate/exercises/exam-submission.component'; import { ExamNavigationBarComponent } from 'app/exam/participate/exam-navigation-bar/exam-navigation-bar.component'; +import { ModelingExamSubmissionComponent } from 'app/exam/participate/exercises/modeling/modeling-exam-submission.component'; +import { QuizExamSubmissionComponent } from 'app/exam/participate/exercises/quiz/quiz-exam-submission.component'; +import { TextExamSubmissionComponent } from 'app/exam/participate/exercises/text/text-exam-submission.component'; import { SubmissionService } from 'app/exercises/shared/submission/submission.service'; import dayjs from 'dayjs/esm'; import { SubmissionVersion } from 'app/entities/submission-version.model'; @@ -15,16 +18,35 @@ import { FileUploadSubmission } from 'app/entities/file-upload-submission.model' import { FileUploadExamSubmissionComponent } from 'app/exam/participate/exercises/file-upload/file-upload-exam-submission.component'; import { SubmissionVersionService } from 'app/exercises/shared/submission-version/submission-version.service'; import { ProgrammingExerciseExamDiffComponent } from 'app/exam/manage/student-exams/student-exam-timeline/programming-exam-diff/programming-exercise-exam-diff.component'; -import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import { ExamPageComponent } from 'app/exam/participate/exercises/exam-page.component'; import { ProgrammingExerciseGitDiffReport } from 'app/entities/programming-exercise-git-diff-report.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { MatSlider, MatSliderThumb } from '@angular/material/slider'; +import { FormsModule } from '@angular/forms'; @Component({ selector: 'jhi-student-exam-timeline', templateUrl: './student-exam-timeline.component.html', styleUrls: ['./student-exam-timeline.component.scss'], + imports: [ + TranslateDirective, + MatSlider, + MatSliderThumb, + FormsModule, + ExamNavigationBarComponent, + QuizExamSubmissionComponent, + FileUploadExamSubmissionComponent, + TextExamSubmissionComponent, + ModelingExamSubmissionComponent, + ProgrammingExerciseExamDiffComponent, + ], }) export class StudentExamTimelineComponent implements OnInit, AfterViewInit, OnDestroy { + private activatedRoute = inject(ActivatedRoute); + private submissionService = inject(SubmissionService); + private submissionVersionService = inject(SubmissionVersionService); + private cdr = inject(ChangeDetectorRef); + readonly ExerciseType = ExerciseType; readonly SubmissionVersion = SubmissionVersion; @@ -52,14 +74,6 @@ export class StudentExamTimelineComponent implements OnInit, AfterViewInit, OnDe private activatedRouteSubscription: Subscription; - constructor( - private activatedRoute: ActivatedRoute, - private submissionService: SubmissionService, - private submissionVersionService: SubmissionVersionService, - private programmingExerciseParticipationService: ProgrammingExerciseParticipationService, - private cdr: ChangeDetectorRef, - ) {} - ngOnInit(): void { this.activatedRouteSubscription = this.activatedRoute.data.subscribe(({ studentExam: studentExamWithGrade }) => { this.studentExam = studentExamWithGrade.studentExam; diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exam.service.ts b/src/main/webapp/app/exam/manage/student-exams/student-exam.service.ts index 8d2196806de9..6a10d4325a96 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exam.service.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exam.service.ts @@ -1,5 +1,4 @@ -import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable, map, tap } from 'rxjs'; import { StudentExam } from 'app/entities/student-exam.model'; @@ -11,13 +10,10 @@ type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class StudentExamService { - public resourceUrl = 'api/courses'; + private http = inject(HttpClient); + private accountService = inject(AccountService); - constructor( - private router: Router, - private http: HttpClient, - private accountService: AccountService, - ) {} + public resourceUrl = 'api/courses'; /** * Find a student exam on the server using a GET request. diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exams.component.html b/src/main/webapp/app/exam/manage/student-exams/student-exams.component.html index 5b85ae4215cd..9b670704024e 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exams.component.html +++ b/src/main/webapp/app/exam/manage/student-exams/student-exams.component.html @@ -162,7 +162,7 @@

    @if (isTestExam) { - + } @if (!isTestExam) { diff --git a/src/main/webapp/app/exam/manage/student-exams/student-exams.component.ts b/src/main/webapp/app/exam/manage/student-exams/student-exams.component.ts index f10fc54ecf2a..e1f30113fd9b 100644 --- a/src/main/webapp/app/exam/manage/student-exams/student-exams.component.ts +++ b/src/main/webapp/app/exam/manage/student-exams/student-exams.component.ts @@ -1,7 +1,9 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; +import { NgbModal, NgbProgressbar } from '@ng-bootstrap/ng-bootstrap'; import { StudentExamService } from 'app/exam/manage/student-exams/student-exam.service'; +import { StudentExamWorkingTimeComponent } from 'app/exam/shared/student-exam-working-time/student-exam-working-time.component'; +import { TestExamWorkingTimeComponent } from 'app/exam/shared/testExam-workingTime/test-exam-working-time.component'; import { Subscription } from 'rxjs'; import { tap } from 'rxjs/operators'; import { StudentExam } from 'app/entities/student-exam.model'; @@ -21,6 +23,12 @@ import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { convertDateFromServer } from 'app/utils/date.utils'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { PROFILE_LOCALVC } from 'app/app.constants'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { StudentExamStatusComponent } from './student-exam-status/student-exam-status.component'; +import { DataTableComponent } from 'app/shared/data-table/data-table.component'; +import { NgxDatatableModule } from '@siemens/ngx-datatable'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; const getWebsocketChannel = (examId: number) => `/topic/exams/${examId}/exercise-start-status`; @@ -35,8 +43,32 @@ export type ExamExerciseStartPreparationStatus = { @Component({ selector: 'jhi-student-exams', templateUrl: './student-exams.component.html', + imports: [ + TranslateDirective, + FaIconComponent, + StudentExamStatusComponent, + NgbProgressbar, + DataTableComponent, + NgxDatatableModule, + RouterLink, + TestExamWorkingTimeComponent, + StudentExamWorkingTimeComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + ], }) export class StudentExamsComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private examManagementService = inject(ExamManagementService); + private studentExamService = inject(StudentExamService); + private courseService = inject(CourseManagementService); + private alertService = inject(AlertService); + private modalService = inject(NgbModal); + private accountService = inject(AccountService); + private artemisTranslatePipe = inject(ArtemisTranslatePipe); + private websocketService = inject(JhiWebsocketService); + private profileService = inject(ProfileService); + courseId: number; examId: number; studentExams: StudentExam[] = []; @@ -63,19 +95,6 @@ export class StudentExamsComponent implements OnInit, OnDestroy { // Icons faExclamationTriangle = faExclamationTriangle; - constructor( - private route: ActivatedRoute, - private examManagementService: ExamManagementService, - private studentExamService: StudentExamService, - private courseService: CourseManagementService, - private alertService: AlertService, - private modalService: NgbModal, - private accountService: AccountService, - private artemisTranslatePipe: ArtemisTranslatePipe, - private websocketService: JhiWebsocketService, - private profileService: ProfileService, - ) {} - /** * Initialize the courseId and examId */ diff --git a/src/main/webapp/app/exam/manage/students/exam-students.component.ts b/src/main/webapp/app/exam/manage/students/exam-students.component.ts index ccc0f9e42671..1d5ea4f81858 100644 --- a/src/main/webapp/app/exam/manage/students/exam-students.component.ts +++ b/src/main/webapp/app/exam/manage/students/exam-students.component.ts @@ -1,8 +1,8 @@ -import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { ExamUser } from 'app/entities/exam/exam-user.model'; import { Observable, Subject, Subscription, of } from 'rxjs'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { User } from 'app/core/user/user.model'; import { ActionType } from 'app/shared/delete-dialog/delete-dialog.model'; import { catchError, map, switchMap, tap } from 'rxjs/operators'; @@ -14,10 +14,16 @@ import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { AccountService } from 'app/core/auth/account.service'; import { AlertService } from 'app/core/util/alert.service'; -import { EventManager } from 'app/core/util/event-manager.service'; import { faCheck, faInfoCircle, faPlus, faTimes, faUpload, faUserSlash, faUserTimes } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { StudentExamService } from 'app/exam/manage/student-exams/student-exam.service'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { UsersImportButtonComponent } from 'app/shared/user-import/users-import-button.component'; +import { StudentsUploadImagesButtonComponent } from './upload-images/students-upload-images-button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { NgxDatatableModule } from '@siemens/ngx-datatable'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; const cssClasses = { alreadyRegistered: 'already-registered', @@ -29,8 +35,26 @@ const cssClasses = { templateUrl: './exam-students.component.html', styleUrls: ['./exam-students.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + TranslateDirective, + UsersImportButtonComponent, + StudentsUploadImagesButtonComponent, + FaIconComponent, + RouterLink, + DeleteButtonDirective, + DataTableComponent, + NgxDatatableModule, + ArtemisTranslatePipe, + ], }) export class ExamStudentsComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + private examManagementService = inject(ExamManagementService); + private userService = inject(UserService); + private accountService = inject(AccountService); + private studentExamService = inject(StudentExamService); + @ViewChild(DataTableComponent) dataTable: DataTableComponent; readonly ButtonType = ButtonType; @@ -67,16 +91,6 @@ export class ExamStudentsComponent implements OnInit, OnDestroy { faUpload = faUpload; faCheck = faCheck; faTimes = faTimes; - constructor( - private router: Router, - private route: ActivatedRoute, - private alertService: AlertService, - private eventManager: EventManager, - private examManagementService: ExamManagementService, - private userService: UserService, - private accountService: AccountService, - private studentExamService: StudentExamService, - ) {} ngOnInit() { this.isLoading = true; diff --git a/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-button.component.ts b/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-button.component.ts index 16b03fe9bbcd..a3d42089b84f 100644 --- a/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-button.component.ts +++ b/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-button.component.ts @@ -1,9 +1,10 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { StudentsUploadImagesDialogComponent } from 'app/exam/manage/students/upload-images/students-upload-images-dialog.component'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { Exam } from 'app/entities/exam/exam.model'; import { faPlus, faUpload } from '@fortawesome/free-solid-svg-icons'; +import { ButtonComponent } from 'app/shared/components/button.component'; @Component({ selector: 'jhi-student-upload-images-button', @@ -16,8 +17,11 @@ import { faPlus, faUpload } from '@fortawesome/free-solid-svg-icons'; (onClick)="openUploadImagesDialog($event)" /> `, + imports: [ButtonComponent], }) export class StudentsUploadImagesButtonComponent { + private modalService = inject(NgbModal); + ButtonType = ButtonType; ButtonSize = ButtonSize; @@ -31,8 +35,6 @@ export class StudentsUploadImagesButtonComponent { faPlus = faPlus; faUpload = faUpload; - constructor(private modalService: NgbModal) {} - /** * Open up upload dialog for exam users image upload * @param {Event} event - Mouse Event which invoked the opening diff --git a/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-dialog.component.ts b/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-dialog.component.ts index f94e4b9f836c..08cd57979d45 100644 --- a/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-dialog.component.ts +++ b/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images-dialog.component.ts @@ -1,5 +1,5 @@ -import { Component, Input, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core'; -import { NgForm } from '@angular/forms'; +import { Component, Input, OnDestroy, ViewChild, ViewEncapsulation, inject } from '@angular/core'; +import { FormsModule, NgForm } from '@angular/forms'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; import { HttpErrorResponse } from '@angular/common/http'; @@ -9,6 +9,11 @@ import { Exam } from 'app/entities/exam/exam.model'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { faArrowRight, faBan, faCheck, faCircleNotch, faSpinner, faUpload } from '@fortawesome/free-solid-svg-icons'; import { onError } from 'app/shared/util/global.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; class NotFoundExamUserType { numberOfUsersNotFound: number; @@ -19,8 +24,13 @@ class NotFoundExamUserType { selector: 'jhi-student-upload-images-dialog', templateUrl: './students-upload-images-dialog.component.html', encapsulation: ViewEncapsulation.None, + imports: [FormsModule, TranslateDirective, HelpIconComponent, FaIconComponent, NgClass, ArtemisTranslatePipe], }) export class StudentsUploadImagesDialogComponent implements OnDestroy { + private activeModal = inject(NgbActiveModal); + private alertService = inject(AlertService); + private examManagementService = inject(ExamManagementService); + readonly ActionType = ActionType; @ViewChild('importForm', { static: false }) importForm: NgForm; @@ -44,12 +54,6 @@ export class StudentsUploadImagesDialogComponent implements OnDestroy { faUpload = faUpload; faArrowRight = faArrowRight; - constructor( - private activeModal: NgbActiveModal, - private alertService: AlertService, - private examManagementService: ExamManagementService, - ) {} - ngOnDestroy(): void { this.dialogErrorSource.unsubscribe(); } diff --git a/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images.module.ts b/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images.module.ts index 59a8f087f2ff..cbad103c8eef 100644 --- a/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images.module.ts +++ b/src/main/webapp/app/exam/manage/students/upload-images/students-upload-images.module.ts @@ -5,8 +5,7 @@ import { ArtemisSharedComponentModule } from 'app/shared/components/shared-compo import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; @NgModule({ - imports: [ArtemisSharedComponentModule, ArtemisSharedCommonModule], - declarations: [StudentsUploadImagesDialogComponent, StudentsUploadImagesButtonComponent], + imports: [ArtemisSharedComponentModule, ArtemisSharedCommonModule, StudentsUploadImagesDialogComponent, StudentsUploadImagesButtonComponent], exports: [StudentsUploadImagesDialogComponent, StudentsUploadImagesButtonComponent], }) export class StudentsUploadImagesModule {} diff --git a/src/main/webapp/app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component.ts b/src/main/webapp/app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component.ts index e8cfdfa14426..519614190490 100644 --- a/src/main/webapp/app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component.ts +++ b/src/main/webapp/app/exam/manage/students/verify-attendance-check/exam-students-attendance-check.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { ExamUserAttendanceCheckDTO } from 'app/entities/exam/exam-users-attendance-check-dto.model'; import { SortService } from 'app/shared/service/sort.service'; @@ -14,13 +14,27 @@ import { AlertService } from 'app/core/util/alert.service'; import { EventManager } from 'app/core/util/event-manager.service'; import { faCheck, faInfoCircle, faPlus, faSort, faTimes, faUpload, faUserSlash, faXmark } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; @Component({ selector: 'jhi-exam-students-attendance-check', templateUrl: './exam-students-attendance-check.component.html', encapsulation: ViewEncapsulation.None, + imports: [TranslateDirective, FaIconComponent, SortDirective, SortByDirective], }) export class ExamStudentsAttendanceCheckComponent implements OnInit, OnDestroy { + private router = inject(Router); + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + private eventManager = inject(EventManager); + private examManagementService = inject(ExamManagementService); + private userService = inject(UserService); + private accountService = inject(AccountService); + private sortService = inject(SortService); + readonly ButtonType = ButtonType; readonly ButtonSize = ButtonSize; readonly ActionType = ActionType; @@ -57,17 +71,6 @@ export class ExamStudentsAttendanceCheckComponent implements OnInit, OnDestroy { faXmark = faXmark; faSort = faSort; - constructor( - private router: Router, - private route: ActivatedRoute, - private alertService: AlertService, - private eventManager: EventManager, - private examManagementService: ExamManagementService, - private userService: UserService, - private accountService: AccountService, - private sortService: SortService, - ) {} - ngOnInit() { this.isLoading = true; this.courseId = Number(this.route.snapshot.paramMap.get('courseId')); diff --git a/src/main/webapp/app/exam/manage/suspicious-behavior/plagiarism-cases-overview/plagiarism-cases-overview.component.ts b/src/main/webapp/app/exam/manage/suspicious-behavior/plagiarism-cases-overview/plagiarism-cases-overview.component.ts index 5f9d2c05a2bd..45d0b248c0bf 100644 --- a/src/main/webapp/app/exam/manage/suspicious-behavior/plagiarism-cases-overview/plagiarism-cases-overview.component.ts +++ b/src/main/webapp/app/exam/manage/suspicious-behavior/plagiarism-cases-overview/plagiarism-cases-overview.component.ts @@ -6,7 +6,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-plagiarism-cases-overview', templateUrl: './plagiarism-cases-overview.component.html', - standalone: true, imports: [TranslateDirective], }) export class PlagiarismCasesOverviewComponent { diff --git a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-behavior.component.ts b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-behavior.component.ts index 0fc847d95581..2fe3406fe655 100644 --- a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-behavior.component.ts +++ b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-behavior.component.ts @@ -16,7 +16,6 @@ import { FormsModule } from '@angular/forms'; @Component({ selector: 'jhi-suspicious-behavior', templateUrl: './suspicious-behavior.component.html', - standalone: true, imports: [FormsModule, TranslateDirective, ArtemisTranslatePipe, ArtemisSharedComponentModule, PlagiarismCasesOverviewComponent], }) export class SuspiciousBehaviorComponent implements OnInit { diff --git a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component.ts b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component.ts index 0626bb6d8911..fa02409b274d 100644 --- a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component.ts +++ b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component.ts @@ -9,7 +9,6 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; selector: 'jhi-suspicious-sessions-overview', templateUrl: './suspicious-sessions-overview.component.html', styleUrls: ['./suspicious-sessions-overview.component.scss'], - standalone: true, imports: [SuspiciousSessionsComponent, TranslateDirective, ArtemisTranslatePipe], }) export class SuspiciousSessionsOverviewComponent implements OnInit { diff --git a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions/suspicious-sessions.component.ts b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions/suspicious-sessions.component.ts index 7e153d9faaad..8cbbe3c92b89 100644 --- a/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions/suspicious-sessions.component.ts +++ b/src/main/webapp/app/exam/manage/suspicious-behavior/suspicious-sessions/suspicious-sessions.component.ts @@ -5,11 +5,9 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; @Component({ // this is intended and an attribute selector because otherwise the rendered table breaks - // eslint-disable-next-line @angular-eslint/component-selector selector: '[jhi-suspicious-sessions]', templateUrl: './suspicious-sessions.component.html', styleUrls: ['./suspicious-sessions.component.scss'], - standalone: true, imports: [ArtemisSharedModule], }) export class SuspiciousSessionsComponent implements OnInit { diff --git a/src/main/webapp/app/exam/manage/test-runs/create-test-run-modal.component.ts b/src/main/webapp/app/exam/manage/test-runs/create-test-run-modal.component.ts index a43fa4a9e41c..29e5e47640a0 100644 --- a/src/main/webapp/app/exam/manage/test-runs/create-test-run-modal.component.ts +++ b/src/main/webapp/app/exam/manage/test-runs/create-test-run-modal.component.ts @@ -1,28 +1,28 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { StudentExam } from 'app/entities/student-exam.model'; import { Exam } from 'app/entities/exam/exam.model'; import { Exercise } from 'app/entities/exercise.model'; import { ExerciseGroup } from 'app/entities/exercise-group.model'; -import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-create-test-run-modal', templateUrl: './create-test-run-modal.component.html', providers: [ArtemisDurationFromSecondsPipe], styles: ['.table tr.active td { background-color:#3e8acc; color: white; }'], + imports: [TranslateDirective, FormsModule, ReactiveFormsModule], }) export class CreateTestRunModalComponent implements OnInit { + private activeModal = inject(NgbActiveModal); + private artemisDurationFromSecondsPipe = inject(ArtemisDurationFromSecondsPipe); + exam: Exam; workingTimeForm: FormGroup; testRunConfiguration: { [id: number]: Exercise } = {}; - constructor( - private activeModal: NgbActiveModal, - private artemisDurationFromSecondsPipe: ArtemisDurationFromSecondsPipe, - ) {} - ngOnInit(): void { this.initWorkingTimeForm(); this.ignoreEmptyExerciseGroups(); diff --git a/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.html b/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.html index 988dcfa83074..6f4971128eac 100644 --- a/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.html +++ b/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.html @@ -113,7 +113,7 @@

    {{ exam? } diff --git a/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.ts b/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.ts index 06d0196d6ba9..0492d8d6ac18 100644 --- a/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.ts +++ b/src/main/webapp/app/exam/manage/test-runs/test-run-management.component.ts @@ -1,12 +1,12 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, RouterLink } from '@angular/router'; import { Course } from 'app/entities/course.model'; import { StudentExam } from 'app/entities/student-exam.model'; import { SortService } from 'app/shared/service/sort.service'; import { Exam } from 'app/entities/exam/exam.model'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbModalRef, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { CreateTestRunModalComponent } from 'app/exam/manage/test-runs/create-test-run-modal.component'; import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { AccountService } from 'app/core/auth/account.service'; @@ -14,12 +14,39 @@ import { Subject } from 'rxjs'; import { User } from 'app/core/user/user.model'; import { onError } from 'app/shared/util/global.utils'; import { faSort, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { ArtemisDurationFromSecondsPipe } from 'app/shared/pipes/artemis-duration-from-seconds.pipe'; @Component({ selector: 'jhi-test-run-management', templateUrl: './test-run-management.component.html', + imports: [ + TranslateDirective, + NgbTooltip, + RouterLink, + SortDirective, + SortByDirective, + FaIconComponent, + DeleteButtonDirective, + ArtemisDatePipe, + ArtemisTranslatePipe, + ArtemisDurationFromSecondsPipe, + ], }) export class TestRunManagementComponent implements OnInit { + private route = inject(ActivatedRoute); + private alertService = inject(AlertService); + private examManagementService = inject(ExamManagementService); + private accountService = inject(AccountService); + private sortService = inject(SortService); + private modalService = inject(NgbModal); + course: Course; exam: Exam; isLoading: boolean; @@ -35,14 +62,7 @@ export class TestRunManagementComponent implements OnInit { faSort = faSort; faTimes = faTimes; - constructor( - private route: ActivatedRoute, - private alertService: AlertService, - private examManagementService: ExamManagementService, - private accountService: AccountService, - private sortService: SortService, - private modalService: NgbModal, - ) { + constructor() { this.predicate = 'id'; this.ascending = true; } diff --git a/src/main/webapp/app/exam/manage/test-runs/test-run-ribbon.component.ts b/src/main/webapp/app/exam/manage/test-runs/test-run-ribbon.component.ts index c2d0b5f5e366..c14f8155ae79 100644 --- a/src/main/webapp/app/exam/manage/test-runs/test-run-ribbon.component.ts +++ b/src/main/webapp/app/exam/manage/test-runs/test-run-ribbon.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-test-run-ribbon', @@ -10,5 +11,6 @@ import { Component } from '@angular/core';

    `, styleUrls: ['../../../shared/layouts/profiles/page-ribbon.scss'], + imports: [TranslateDirective], }) export class TestRunRibbonComponent {} diff --git a/src/main/webapp/app/exam/participate/events/exam-live-events-button.component.ts b/src/main/webapp/app/exam/participate/events/exam-live-events-button.component.ts index 8659685258a2..91a0fcf88fa8 100644 --- a/src/main/webapp/app/exam/participate/events/exam-live-events-button.component.ts +++ b/src/main/webapp/app/exam/participate/events/exam-live-events-button.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, ViewEncapsulation, inject } from '@angular/core'; import { faBullhorn } from '@fortawesome/free-solid-svg-icons'; import { AlertService } from 'app/core/util/alert.service'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; @@ -6,6 +6,7 @@ import { Subscription, from } from 'rxjs'; import { ExamLiveEvent, ExamLiveEventType, ExamParticipationLiveEventsService } from 'app/exam/participate/exam-participation-live-events.service'; import { ExamLiveEventsOverlayComponent } from 'app/exam/participate/events/exam-live-events-overlay.component'; import dayjs from 'dayjs/esm'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; export const USER_DISPLAY_RELEVANT_EVENTS = [ ExamLiveEventType.EXAM_WIDE_ANNOUNCEMENT, @@ -20,8 +21,13 @@ export const USER_DISPLAY_RELEVANT_EVENTS_REOPEN = [ExamLiveEventType.EXAM_WIDE_ templateUrl: './exam-live-events-button.component.html', styleUrls: ['./exam-live-events-button.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [FaIconComponent], }) export class ExamLiveEventsButtonComponent implements OnInit, OnDestroy { + private alertService = inject(AlertService); + private modalService = inject(NgbModal); + private liveEventsService = inject(ExamParticipationLiveEventsService); + private modalRef?: NgbModalRef; private liveEventsSubscription?: Subscription; private allEventsSubscription?: Subscription; @@ -31,12 +37,6 @@ export class ExamLiveEventsButtonComponent implements OnInit, OnDestroy { // Icons faBullhorn = faBullhorn; - constructor( - private alertService: AlertService, - private modalService: NgbModal, - private liveEventsService: ExamParticipationLiveEventsService, - ) {} - ngOnInit(): void { this.allEventsSubscription = this.liveEventsService.observeAllEvents(USER_DISPLAY_RELEVANT_EVENTS_REOPEN).subscribe((events: ExamLiveEvent[]) => { // do not count the problem statements events that are made before the start of the exam diff --git a/src/main/webapp/app/exam/participate/events/exam-live-events-overlay.component.ts b/src/main/webapp/app/exam/participate/events/exam-live-events-overlay.component.ts index cf24ccb614e4..b1c1e7f99c1c 100644 --- a/src/main/webapp/app/exam/participate/events/exam-live-events-overlay.component.ts +++ b/src/main/webapp/app/exam/participate/events/exam-live-events-overlay.component.ts @@ -1,18 +1,26 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { faCheck } from '@fortawesome/free-solid-svg-icons'; +import { ExamLiveEventComponent } from 'app/exam/shared/events/exam-live-event.component'; import { Subscription } from 'rxjs'; import { ExamLiveEvent, ExamLiveEventType, ExamParticipationLiveEventsService, ProblemStatementUpdateEvent } from 'app/exam/participate/exam-participation-live-events.service'; import { USER_DISPLAY_RELEVANT_EVENTS, USER_DISPLAY_RELEVANT_EVENTS_REOPEN } from 'app/exam/participate/events/exam-live-events-button.component'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ExamExerciseUpdateService } from 'app/exam/manage/exam-exercise-update.service'; import dayjs from 'dayjs/esm'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-exam-live-events-overlay', templateUrl: './exam-live-events-overlay.component.html', styleUrls: ['./exam-live-events-overlay.component.scss'], + imports: [ExamLiveEventComponent, FaIconComponent, TranslateDirective], }) export class ExamLiveEventsOverlayComponent implements OnInit, OnDestroy { + private liveEventsService = inject(ExamParticipationLiveEventsService); + private activeModal = inject(NgbActiveModal); + private examExerciseUpdateService = inject(ExamExerciseUpdateService); + private allLiveEventsSubscription?: Subscription; private newLiveEventsSubscription?: Subscription; @@ -26,12 +34,6 @@ export class ExamLiveEventsOverlayComponent implements OnInit, OnDestroy { protected readonly ExamLiveEventType = ExamLiveEventType; - constructor( - private liveEventsService: ExamParticipationLiveEventsService, - private activeModal: NgbActiveModal, - private examExerciseUpdateService: ExamExerciseUpdateService, - ) {} - ngOnDestroy(): void { this.allLiveEventsSubscription?.unsubscribe(); this.newLiveEventsSubscription?.unsubscribe(); diff --git a/src/main/webapp/app/exam/participate/events/exam-live-events.module.ts b/src/main/webapp/app/exam/participate/events/exam-live-events.module.ts index ef1a24e8e765..cc805d9c46f0 100644 --- a/src/main/webapp/app/exam/participate/events/exam-live-events.module.ts +++ b/src/main/webapp/app/exam/participate/events/exam-live-events.module.ts @@ -7,8 +7,7 @@ import { ExamLiveEventsOverlayComponent } from 'app/exam/participate/events/exam import { ArtemisExamSharedModule } from 'app/exam/shared/exam-shared.module'; @NgModule({ - declarations: [ExamLiveEventsButtonComponent, ExamLiveEventsOverlayComponent], - imports: [CommonModule, ArtemisSharedCommonModule, ArtemisExamTimerModule, ArtemisExamSharedModule], + imports: [CommonModule, ArtemisSharedCommonModule, ArtemisExamTimerModule, ArtemisExamSharedModule, ExamLiveEventsButtonComponent, ExamLiveEventsOverlayComponent], exports: [ExamLiveEventsButtonComponent, ExamLiveEventsOverlayComponent], }) export class ArtemisExamLiveEventsModule {} diff --git a/src/main/webapp/app/exam/participate/exam-bar/exam-bar.component.ts b/src/main/webapp/app/exam/participate/exam-bar/exam-bar.component.ts index daf2532e3b15..e4c0b2afacf5 100644 --- a/src/main/webapp/app/exam/participate/exam-bar/exam-bar.component.ts +++ b/src/main/webapp/app/exam/participate/exam-bar/exam-bar.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; import { ArtemisExamTimerModule } from 'app/exam/participate/timer/exam-timer.module'; @@ -12,12 +12,13 @@ import { StudentExam } from 'app/entities/student-exam.model'; @Component({ selector: 'jhi-exam-bar', - standalone: true, imports: [CommonModule, ArtemisSharedCommonModule, ArtemisExamTimerModule, ArtemisExamLiveEventsModule], templateUrl: './exam-bar.component.html', styleUrl: './exam-bar.component.scss', }) export class ExamBarComponent implements AfterViewInit, OnInit { + private elementRef = inject(ElementRef); + @Output() onExamHandInEarly = new EventEmitter(); @Output() examAboutToEnd = new EventEmitter(); @Output() heightChange = new EventEmitter(); @@ -41,8 +42,6 @@ export class ExamBarComponent implements AfterViewInit, OnInit { examTitle: string; exercises: Exercise[] = []; - constructor(private elementRef: ElementRef) {} - ngOnInit(): void { this.examTitle = this.exam.title ?? ''; this.exercises = this.studentExam.exercises ?? []; diff --git a/src/main/webapp/app/exam/participate/exam-cover/exam-participation-cover.component.ts b/src/main/webapp/app/exam/participate/exam-cover/exam-participation-cover.component.ts index 090588335574..80c1d82d931d 100644 --- a/src/main/webapp/app/exam/participate/exam-cover/exam-participation-cover.component.ts +++ b/src/main/webapp/app/exam/participate/exam-cover/exam-participation-cover.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, inject } from '@angular/core'; import { SafeHtml } from '@angular/platform-browser'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { CourseManagementService } from 'app/course/manage/course-management.service'; @@ -14,13 +14,29 @@ import { EXAM_START_WAIT_TIME_MINUTES } from 'app/app.constants'; import { UI_RELOAD_TIME } from 'app/shared/constants/exercise-exam-constants'; import { faArrowLeft, faCircleExclamation, faDoorClosed, faSpinner } from '@fortawesome/free-solid-svg-icons'; import { Subscription } from 'rxjs'; +import { NgClass } from '@angular/common'; +import { ExamLiveEventsButtonComponent } from '../events/exam-live-events-button.component'; +import { ExamStartInformationComponent } from '../exam-start-information/exam-start-information.component'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exam-participation-cover', templateUrl: './exam-participation-cover.component.html', styleUrls: ['./exam-participation-cover.scss'], + imports: [NgClass, ExamLiveEventsButtonComponent, ExamStartInformationComponent, FormsModule, TranslateDirective, FaIconComponent, ArtemisDatePipe, ArtemisTranslatePipe], }) export class ExamParticipationCoverComponent implements OnChanges, OnDestroy, OnInit { + private courseService = inject(CourseManagementService); + private artemisMarkdown = inject(ArtemisMarkdownService); + private translateService = inject(TranslateService); + private accountService = inject(AccountService); + private examParticipationService = inject(ExamParticipationService); + private serverDateService = inject(ArtemisServerDateService); + /** * if startView is set to true: startText and confirmationStartText will be displayed * if startView is set to false: endText and confirmationEndText will be displayed @@ -65,15 +81,6 @@ export class ExamParticipationCoverComponent implements OnChanges, OnDestroy, On faCircleExclamation = faCircleExclamation; faDoorClosed = faDoorClosed; - constructor( - private courseService: CourseManagementService, - private artemisMarkdown: ArtemisMarkdownService, - private translateService: TranslateService, - private accountService: AccountService, - private examParticipationService: ExamParticipationService, - private serverDateService: ArtemisServerDateService, - ) {} - ngOnInit(): void { this.isAttendanceChecked = this.exam.testExam || !this.exam.examWithAttendanceCheck || this.attendanceChecked; } @@ -83,7 +90,7 @@ export class ExamParticipationCoverComponent implements OnChanges, OnDestroy, On * changes in the exam and subscription is handled in the exam-participation.component * if the student exam changes, we need to update the displayed times */ - ngOnChanges(): void { + ngOnChanges() { this.confirmed = false; this.startEnabled = false; this.testRun = this.studentExam.testRun; diff --git a/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.component.ts b/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.component.ts index 800285feeda1..28aab3a490b3 100644 --- a/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.component.ts +++ b/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { LayoutService } from 'app/shared/breakpoints/layout.service'; @@ -17,13 +17,27 @@ import { faBars, faCheck, faEdit } from '@fortawesome/free-solid-svg-icons'; import { ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { SubmissionVersion } from 'app/entities/submission-version.model'; import { FileUploadSubmission } from 'app/entities/file-upload-submission.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ExamLiveEventsButtonComponent } from '../events/exam-live-events-button.component'; +import { NgClass } from '@angular/common'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ExamTimerComponent } from '../timer/exam-timer.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exam-navigation-bar', templateUrl: './exam-navigation-bar.component.html', styleUrls: ['./exam-navigation-bar.component.scss'], + imports: [TranslateDirective, ExamLiveEventsButtonComponent, NgClass, NgbTooltip, FaIconComponent, ExamTimerComponent, ArtemisTranslatePipe], }) export class ExamNavigationBarComponent implements OnInit, AfterViewInit { + private layoutService = inject(LayoutService); + private examParticipationService = inject(ExamParticipationService); + private examExerciseUpdateService = inject(ExamExerciseUpdateService); + private repositoryService = inject(CodeEditorRepositoryService); + private conflictService = inject(CodeEditorConflictStateService); + @Input() exercises: Exercise[] = []; @Input() exerciseIndex = 0; @Input() endDate: dayjs.Dayjs; @@ -51,14 +65,6 @@ export class ExamNavigationBarComponent implements OnInit, AfterViewInit { // Icons faBars = faBars; - constructor( - private layoutService: LayoutService, - private examParticipationService: ExamParticipationService, - private examExerciseUpdateService: ExamExerciseUpdateService, - private repositoryService: CodeEditorRepositoryService, - private conflictService: CodeEditorConflictStateService, - ) {} - ngOnInit(): void { if (!this.examTimeLineView) { this.subscriptionToLiveExamExerciseUpdates = this.examExerciseUpdateService.currentExerciseIdForNavigation.subscribe((exerciseIdToNavigateTo) => { diff --git a/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.module.ts b/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.module.ts index 1dedc313ae2e..e56eeac54ab9 100644 --- a/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.module.ts +++ b/src/main/webapp/app/exam/participate/exam-navigation-bar/exam-navigation-bar.module.ts @@ -6,8 +6,7 @@ import { ArtemisExamTimerModule } from 'app/exam/participate/timer/exam-timer.mo import { ArtemisExamLiveEventsModule } from 'app/exam/participate/events/exam-live-events.module'; @NgModule({ - declarations: [ExamNavigationBarComponent], - imports: [CommonModule, ArtemisSharedCommonModule, ArtemisExamTimerModule, ArtemisExamLiveEventsModule], + imports: [CommonModule, ArtemisSharedCommonModule, ArtemisExamTimerModule, ArtemisExamLiveEventsModule, ExamNavigationBarComponent], exports: [ExamNavigationBarComponent], }) export class ArtemisExamNavigationBarModule {} diff --git a/src/main/webapp/app/exam/participate/exam-navigation-sidebar/exam-navigation-sidebar.component.ts b/src/main/webapp/app/exam/participate/exam-navigation-sidebar/exam-navigation-sidebar.component.ts index 93bcecfa27ae..adbfb0e9ca91 100644 --- a/src/main/webapp/app/exam/participate/exam-navigation-sidebar/exam-navigation-sidebar.component.ts +++ b/src/main/webapp/app/exam/participate/exam-navigation-sidebar/exam-navigation-sidebar.component.ts @@ -1,7 +1,6 @@ -import { Component, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, HostListener, Input, OnDestroy, OnInit, Output, inject } from '@angular/core'; import { ArtemisSidebarModule } from 'app/shared/sidebar/sidebar.module'; import { ArtemisSharedModule } from 'app/shared/shared.module'; -import { SidebarCardDirective } from 'app/shared/sidebar/sidebar-card.directive'; import { Subscription } from 'rxjs'; import { SidebarEventService } from 'app/shared/sidebar/sidebar-event.service'; import { SidebarData } from 'app/types/sidebar'; @@ -20,8 +19,8 @@ import { CommitState, DomainChange, DomainType } from 'app/exercises/programming import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { faChevronRight, faFileLines, faHourglassHalf } from '@fortawesome/free-solid-svg-icons'; -import { facSaveSuccess, facSaveWarning } from '../../../../content/icons/icons'; import { getIconTooltip } from 'app/entities/exercise.model'; +import { facSaveSuccess, facSaveWarning } from 'app/icons/icons'; export enum ExerciseButtonStatus { Synced = 'synced', @@ -31,12 +30,18 @@ export enum ExerciseButtonStatus { @Component({ selector: 'jhi-exam-navigation-sidebar', - standalone: true, - imports: [ArtemisSidebarModule, ArtemisSharedModule, SidebarCardDirective], + imports: [ArtemisSidebarModule, ArtemisSharedModule], templateUrl: './exam-navigation-sidebar.component.html', styleUrl: './exam-navigation-sidebar.component.scss', }) export class ExamNavigationSidebarComponent implements OnDestroy, OnInit { + private profileService = inject(ProfileService); + private sidebarEventService = inject(SidebarEventService); + private examParticipationService = inject(ExamParticipationService); + private examExerciseUpdateService = inject(ExamExerciseUpdateService); + private repositoryService = inject(CodeEditorRepositoryService); + private conflictService = inject(CodeEditorConflictStateService); + @Input() sidebarData: SidebarData; @Input() exercises: Exercise[] = []; @Input() exerciseIndex = 0; @@ -61,24 +66,14 @@ export class ExamNavigationSidebarComponent implements OnDestroy, OnInit { icon: IconProp; readonly faFileLines = faFileLines; readonly faChevronRight = faChevronRight; - readonly ExerciseButtonStatus = ExerciseButtonStatus; profileSubscription?: Subscription; isProduction = true; isTestServer = false; - isCollapsed: boolean = false; + isCollapsed = false; exerciseId: string; numberOfSavedExercises: number = 0; - constructor( - private profileService: ProfileService, - private sidebarEventService: SidebarEventService, - private examParticipationService: ExamParticipationService, - private examExerciseUpdateService: ExamExerciseUpdateService, - private repositoryService: CodeEditorRepositoryService, - private conflictService: CodeEditorConflictStateService, - ) {} - ngOnInit(): void { this.profileSubscription = this.profileService.getProfileInfo()?.subscribe((profileInfo) => { this.isProduction = profileInfo?.inProduction; @@ -92,6 +87,7 @@ export class ExamNavigationSidebarComponent implements OnDestroy, OnInit { }); } + // TODO: avoid duplicated code const isInitialSession = this.examSessions && this.examSessions.length > 0 && this.examSessions[0].initialSession; if (isInitialSession || isInitialSession == undefined) { return; @@ -134,9 +130,9 @@ export class ExamNavigationSidebarComponent implements OnDestroy, OnInit { } /** - * @param overviewPage: user wants to switch to the overview page - * @param exerciseIndex: index of the exercise to switch to, if it should not be used, you can pass -1 - * @param forceSave: true if forceSave shall be used. + * @param overviewPage user wants to switch to the overview page + * @param exerciseIndex index of the exercise to switch to, if it should not be used, you can pass -1 + * @param forceSave true if forceSave shall be used. * @param submission the submission to be viewed, used in the exam timeline */ changePage(overviewPage: boolean, exerciseIndex: number, forceSave?: boolean, submission?: SubmissionVersion | ProgrammingSubmission | FileUploadSubmission): void { diff --git a/src/main/webapp/app/exam/participate/exam-participation-live-events.service.ts b/src/main/webapp/app/exam/participate/exam-participation-live-events.service.ts index 63e5c7cf0ec8..1058fb8fbbba 100644 --- a/src/main/webapp/app/exam/participate/exam-participation-live-events.service.ts +++ b/src/main/webapp/app/exam/participate/exam-participation-live-events.service.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ConnectionState, JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { ExamParticipationService } from 'app/exam/participate/exam-participation.service'; import dayjs from 'dayjs/esm'; @@ -54,6 +54,11 @@ export type ProblemStatementUpdateEvent = ExamLiveEvent & { @Injectable({ providedIn: 'root' }) export class ExamParticipationLiveEventsService { + private websocketService = inject(JhiWebsocketService); + private examParticipationService = inject(ExamParticipationService); + private localStorageService = inject(LocalStorageService); + private httpClient = inject(HttpClient); + private courseId?: number; private examId?: number; private studentExamId?: number; @@ -74,12 +79,7 @@ export class ExamParticipationLiveEventsService { // Subject that emits all events when the array of events changes private allEventsSubject = new BehaviorSubject([]); - constructor( - private websocketService: JhiWebsocketService, - private examParticipationService: ExamParticipationService, - private localStorageService: LocalStorageService, - private httpClient: HttpClient, - ) { + constructor() { this.clearOldAcknowledgement(); // Listen to updates of the connection state; if we reconnect, we should fetch the list of events diff --git a/src/main/webapp/app/exam/participate/exam-participation.component.ts b/src/main/webapp/app/exam/participate/exam-participation.component.ts index 7871ce88d681..c4b8c658aff2 100644 --- a/src/main/webapp/app/exam/participate/exam-participation.component.ts +++ b/src/main/webapp/app/exam/participate/exam-participation.component.ts @@ -1,5 +1,5 @@ -import { Component, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { Component, OnDestroy, OnInit, QueryList, ViewChildren, inject } from '@angular/core'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { ExamParticipationService } from 'app/exam/participate/exam-participation.service'; import { StudentExam } from 'app/entities/student-exam.model'; @@ -45,6 +45,22 @@ import { import { ExamExerciseUpdateService } from 'app/exam/manage/exam-exercise-update.service'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { SidebarCardElement, SidebarData } from 'app/types/sidebar'; +import { TestRunRibbonComponent } from '../manage/test-runs/test-run-ribbon.component'; +import { ExamParticipationCoverComponent } from './exam-cover/exam-participation-cover.component'; +import { AsyncPipe, NgClass } from '@angular/common'; +import { ExamBarComponent } from './exam-bar/exam-bar.component'; +import { ExamNavigationSidebarComponent } from './exam-navigation-sidebar/exam-navigation-sidebar.component'; +import { ExamExerciseOverviewPageComponent } from './exercises/exercise-overview-page/exam-exercise-overview-page.component'; +import { QuizExamSubmissionComponent } from './exercises/quiz/quiz-exam-submission.component'; +import { FileUploadExamSubmissionComponent } from './exercises/file-upload/file-upload-exam-submission.component'; +import { TextExamSubmissionComponent } from './exercises/text/text-exam-submission.component'; +import { ModelingExamSubmissionComponent } from './exercises/modeling/modeling-exam-submission.component'; +import { ProgrammingExamSubmissionComponent } from './exercises/programming/programming-exam-submission.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { JhiConnectionStatusComponent } from 'app/shared/connection-status/connection-status.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ExamResultSummaryComponent } from './summary/exam-result-summary.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; type GenerateParticipationStatus = 'generating' | 'failed' | 'success'; @@ -52,8 +68,46 @@ type GenerateParticipationStatus = 'generating' | 'failed' | 'success'; selector: 'jhi-exam-participation', templateUrl: './exam-participation.component.html', styleUrls: ['./exam-participation.scss'], + imports: [ + TestRunRibbonComponent, + ExamParticipationCoverComponent, + NgClass, + ExamBarComponent, + ExamNavigationSidebarComponent, + ExamExerciseOverviewPageComponent, + QuizExamSubmissionComponent, + FileUploadExamSubmissionComponent, + TextExamSubmissionComponent, + ModelingExamSubmissionComponent, + ProgrammingExamSubmissionComponent, + TranslateDirective, + JhiConnectionStatusComponent, + FaIconComponent, + ExamResultSummaryComponent, + RouterLink, + AsyncPipe, + ArtemisTranslatePipe, + ], }) export class ExamParticipationComponent implements OnInit, OnDestroy, ComponentCanDeactivate { + private websocketService = inject(JhiWebsocketService); + private route = inject(ActivatedRoute); + private router = inject(Router); + private examParticipationService = inject(ExamParticipationService); + private modelingSubmissionService = inject(ModelingSubmissionService); + private programmingSubmissionService = inject(ProgrammingSubmissionService); + private textSubmissionService = inject(TextSubmissionService); + private serverDateService = inject(ArtemisServerDateService); + private translateService = inject(TranslateService); + private alertService = inject(AlertService); + private courseExerciseService = inject(CourseExerciseService); + private liveEventsService = inject(ExamParticipationLiveEventsService); + private courseService = inject(CourseManagementService); + private courseStorageService = inject(CourseStorageService); + private examExerciseUpdateService = inject(ExamExerciseUpdateService); + private examManagementService = inject(ExamManagementService); + private profileService = inject(ProfileService); + @ViewChildren(ExamSubmissionComponent) currentPageComponents: QueryList; @@ -145,25 +199,7 @@ export class ExamParticipationComponent implements OnInit, OnDestroy, ComponentC // Icons faGraduationCap = faGraduationCap; - constructor( - private websocketService: JhiWebsocketService, - private route: ActivatedRoute, - private router: Router, - private examParticipationService: ExamParticipationService, - private modelingSubmissionService: ModelingSubmissionService, - private programmingSubmissionService: ProgrammingSubmissionService, - private textSubmissionService: TextSubmissionService, - private serverDateService: ArtemisServerDateService, - private translateService: TranslateService, - private alertService: AlertService, - private courseExerciseService: CourseExerciseService, - private liveEventsService: ExamParticipationLiveEventsService, - private courseService: CourseManagementService, - private courseStorageService: CourseStorageService, - private examExerciseUpdateService: ExamExerciseUpdateService, - private examManagementService: ExamManagementService, - private profileService: ProfileService, - ) { + constructor() { // show only one synchronization error every 5s this.errorSubscription = this.synchronizationAlert.pipe(throttleTime(5000)).subscribe(() => { this.alertService.error('artemisApp.examParticipation.saveSubmissionError'); diff --git a/src/main/webapp/app/exam/participate/exam-participation.module.ts b/src/main/webapp/app/exam/participate/exam-participation.module.ts index 2aac8a643f4f..2ba50e3dd5d1 100644 --- a/src/main/webapp/app/exam/participate/exam-participation.module.ts +++ b/src/main/webapp/app/exam/participate/exam-participation.module.ts @@ -64,7 +64,9 @@ const ENTITY_STATES = [...examParticipationState]; ArtemisSidebarModule, ExamNavigationSidebarComponent, ExamBarComponent, + ExamParticipationComponent, + ExamParticipationCoverComponent, + ExamExerciseOverviewPageComponent, ], - declarations: [ExamParticipationComponent, ExamParticipationCoverComponent, ExamExerciseOverviewPageComponent], }) export class ArtemisExamParticipationModule {} diff --git a/src/main/webapp/app/exam/participate/exam-participation.route.ts b/src/main/webapp/app/exam/participate/exam-participation.route.ts index a104b0ad964e..dfced7d039e0 100644 --- a/src/main/webapp/app/exam/participate/exam-participation.route.ts +++ b/src/main/webapp/app/exam/participate/exam-participation.route.ts @@ -1,15 +1,13 @@ import { Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { ExamParticipationComponent } from 'app/exam/participate/exam-participation.component'; + import { PendingChangesGuard } from 'app/shared/guard/pending-changes.guard'; import { Authority } from 'app/shared/constants/authority.constants'; -import { GradingKeyOverviewComponent } from 'app/grading-system/grading-key-overview/grading-key-overview.component'; -import { ExampleSolutionComponent } from 'app/exercises/shared/example-solution/example-solution.component'; export const examParticipationRoute: Routes = [ { path: '', - component: ExamParticipationComponent, + loadComponent: () => import('app/exam/participate/exam-participation.component').then((m) => m.ExamParticipationComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.exam.title', @@ -18,7 +16,7 @@ export const examParticipationRoute: Routes = [ }, { path: 'overview/grading-key', - component: GradingKeyOverviewComponent, + loadComponent: () => import('app/grading-system/grading-key-overview/grading-key-overview.component').then((m) => m.GradingKeyOverviewComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.exam.title', @@ -27,7 +25,7 @@ export const examParticipationRoute: Routes = [ }, { path: 'overview/bonus-grading-key', - component: GradingKeyOverviewComponent, + loadComponent: () => import('app/grading-system/grading-key-overview/grading-key-overview.component').then((m) => m.GradingKeyOverviewComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.exam.title', @@ -37,7 +35,7 @@ export const examParticipationRoute: Routes = [ }, { path: 'test-exam/:studentExamId', - component: ExamParticipationComponent, + loadComponent: () => import('app/exam/participate/exam-participation.component').then((m) => m.ExamParticipationComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.exam.title', @@ -47,7 +45,7 @@ export const examParticipationRoute: Routes = [ }, { path: 'exercises/:exerciseId/example-solution', - component: ExampleSolutionComponent, + loadComponent: () => import('app/exercises/shared/example-solution/example-solution.component').then((m) => m.ExampleSolutionComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.exam.title', diff --git a/src/main/webapp/app/exam/participate/exam-participation.service.ts b/src/main/webapp/app/exam/participate/exam-participation.service.ts index 5e125f6b1288..4add2a0fd022 100644 --- a/src/main/webapp/app/exam/participate/exam-participation.service.ts +++ b/src/main/webapp/app/exam/participate/exam-participation.service.ts @@ -1,5 +1,5 @@ import { HttpClient, HttpErrorResponse, HttpParams, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { faLightbulb } from '@fortawesome/free-solid-svg-icons'; import { captureException } from '@sentry/angular'; import { Exam } from 'app/entities/exam/exam.model'; @@ -22,6 +22,10 @@ export type ButtonTooltipType = 'submitted' | 'submittedSubmissionLimitReached' @Injectable({ providedIn: 'root' }) export class ExamParticipationService { + private httpClient = inject(HttpClient); + private localStorageService = inject(LocalStorageService); + private sessionStorage = inject(SessionStorageService); + public currentlyLoadedStudentExam = new Subject(); private examIsStartedSubject = new BehaviorSubject(false); @@ -37,12 +41,6 @@ export class ExamParticipationService { return `api/courses/${courseId}/exams/${examId}`; } - constructor( - private httpClient: HttpClient, - private localStorageService: LocalStorageService, - private sessionStorage: SessionStorageService, - ) {} - private static getLocalStorageKeyForStudentExam(courseId: number, examId: number): string { const prefix = 'artemis_student_exam'; return `${prefix}_${courseId}_${examId}`; diff --git a/src/main/webapp/app/exam/participate/exam-start-information/exam-start-information.component.ts b/src/main/webapp/app/exam/participate/exam-start-information/exam-start-information.component.ts index 3f02da6bab2b..6e56677edc85 100644 --- a/src/main/webapp/app/exam/participate/exam-start-information/exam-start-information.component.ts +++ b/src/main/webapp/app/exam/participate/exam-start-information/exam-start-information.component.ts @@ -10,7 +10,6 @@ import { SafeHtml } from '@angular/platform-browser'; @Component({ selector: 'jhi-exam-start-information', - standalone: true, imports: [ArtemisSharedModule, ArtemisSharedComponentModule, InformationBoxComponent, ArtemisExamSharedModule], templateUrl: './exam-start-information.component.html', }) diff --git a/src/main/webapp/app/exam/participate/exercises/exam-exercise-update-highlighter/exam-exercise-update-highlighter.component.ts b/src/main/webapp/app/exam/participate/exercises/exam-exercise-update-highlighter/exam-exercise-update-highlighter.component.ts index e841254ef587..35ad5bd48745 100644 --- a/src/main/webapp/app/exam/participate/exercises/exam-exercise-update-highlighter/exam-exercise-update-highlighter.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/exam-exercise-update-highlighter/exam-exercise-update-highlighter.component.ts @@ -1,16 +1,20 @@ -import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, inject } from '@angular/core'; import { Subscription } from 'rxjs'; import { ExamExerciseUpdateService } from 'app/exam/manage/exam-exercise-update.service'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { htmlForMarkdown } from 'app/shared/util/markdown.conversion.util'; import diff from 'html-diff-ts'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exam-exercise-update-highlighter', templateUrl: './exam-exercise-update-highlighter.component.html', styleUrls: ['./exam-exercise-update-highlighter.component.scss'], + imports: [ArtemisTranslatePipe], }) export class ExamExerciseUpdateHighlighterComponent implements OnInit, OnDestroy { + private examExerciseUpdateService = inject(ExamExerciseUpdateService); + subscriptionToLiveExamExerciseUpdates: Subscription; themeSubscription: Subscription; updatedProblemStatementHTML: string; @@ -23,8 +27,6 @@ export class ExamExerciseUpdateHighlighterComponent implements OnInit, OnDestroy @Output() problemStatementUpdateEvent: EventEmitter = new EventEmitter(); - constructor(private examExerciseUpdateService: ExamExerciseUpdateService) {} - ngOnInit(): void { this.subscriptionToLiveExamExerciseUpdates = this.examExerciseUpdateService.currentExerciseIdAndProblemStatement.subscribe((update) => { if (update) { diff --git a/src/main/webapp/app/exam/participate/exercises/exam-exercise-update-highlighter/exam-exercise-update-highlighter.module.ts b/src/main/webapp/app/exam/participate/exercises/exam-exercise-update-highlighter/exam-exercise-update-highlighter.module.ts index 30e5255744b1..e915682f2001 100644 --- a/src/main/webapp/app/exam/participate/exercises/exam-exercise-update-highlighter/exam-exercise-update-highlighter.module.ts +++ b/src/main/webapp/app/exam/participate/exercises/exam-exercise-update-highlighter/exam-exercise-update-highlighter.module.ts @@ -3,8 +3,7 @@ import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; import { ExamExerciseUpdateHighlighterComponent } from 'app/exam/participate/exercises/exam-exercise-update-highlighter/exam-exercise-update-highlighter.component'; @NgModule({ - declarations: [ExamExerciseUpdateHighlighterComponent], - imports: [ArtemisSharedCommonModule], + imports: [ArtemisSharedCommonModule, ExamExerciseUpdateHighlighterComponent], exports: [ExamExerciseUpdateHighlighterComponent], }) export class ExamExerciseUpdateHighlighterModule {} diff --git a/src/main/webapp/app/exam/participate/exercises/exam-page.component.ts b/src/main/webapp/app/exam/participate/exercises/exam-page.component.ts index d587462715b6..cf2130c6236a 100644 --- a/src/main/webapp/app/exam/participate/exercises/exam-page.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/exam-page.component.ts @@ -1,8 +1,8 @@ -import { ChangeDetectorRef, Directive } from '@angular/core'; +import { ChangeDetectorRef, Directive, inject } from '@angular/core'; @Directive() export abstract class ExamPageComponent { - protected constructor(protected changeDetectorReference: ChangeDetectorRef) {} + protected changeDetectorReference = inject(ChangeDetectorRef); /** * Should be called when the component becomes active / visible. It activates Angular's change detection for this component. diff --git a/src/main/webapp/app/exam/participate/exercises/exam-submission-components.module.ts b/src/main/webapp/app/exam/participate/exercises/exam-submission-components.module.ts index 72fb6667e806..9d374e6f90b0 100644 --- a/src/main/webapp/app/exam/participate/exercises/exam-submission-components.module.ts +++ b/src/main/webapp/app/exam/participate/exercises/exam-submission-components.module.ts @@ -21,13 +21,6 @@ import { ArtemisProgrammingSubmissionPolicyStatusModule } from 'app/exercises/pr import { ExerciseSaveButtonComponent } from './exercise-save-button/exercise-save-button.component'; @NgModule({ - declarations: [ - FileUploadExamSubmissionComponent, - QuizExamSubmissionComponent, - ProgrammingExamSubmissionComponent, - TextExamSubmissionComponent, - ModelingExamSubmissionComponent, - ], imports: [ CommonModule, ArtemisSharedModule, @@ -44,6 +37,11 @@ import { ExerciseSaveButtonComponent } from './exercise-save-button/exercise-sav ArtemisProgrammingSubmissionPolicyStatusModule, ExamExerciseUpdateHighlighterModule, ExerciseSaveButtonComponent, + FileUploadExamSubmissionComponent, + QuizExamSubmissionComponent, + ProgrammingExamSubmissionComponent, + TextExamSubmissionComponent, + ModelingExamSubmissionComponent, ], exports: [FileUploadExamSubmissionComponent, QuizExamSubmissionComponent, ProgrammingExamSubmissionComponent, TextExamSubmissionComponent, ModelingExamSubmissionComponent], }) diff --git a/src/main/webapp/app/exam/participate/exercises/exercise-overview-page/exam-exercise-overview-page.component.ts b/src/main/webapp/app/exam/participate/exercises/exercise-overview-page/exam-exercise-overview-page.component.ts index 483891aba2c1..3d6d75aaca8b 100644 --- a/src/main/webapp/app/exam/participate/exercises/exercise-overview-page/exam-exercise-overview-page.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/exercise-overview-page/exam-exercise-overview-page.component.ts @@ -1,35 +1,36 @@ -import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, inject } from '@angular/core'; import { Exercise, ExerciseType, getIcon, getIconTooltip } from 'app/entities/exercise.model'; import { ExamPageComponent } from 'app/exam/participate/exercises/exam-page.component'; import { StudentExam } from 'app/entities/student-exam.model'; import { ExamExerciseOverviewItem } from 'app/entities/exam/exam-exercise-overview-item.model'; import { ButtonTooltipType, ExamParticipationService } from 'app/exam/participate/exam-participation.service'; import { faHourglassHalf } from '@fortawesome/free-solid-svg-icons'; -import { facSaveSuccess, facSaveWarning } from '../../../../../content/icons/icons'; import { ExerciseButtonStatus } from 'app/exam/participate/exam-navigation-sidebar/exam-navigation-sidebar.component'; +import { facSaveSuccess, facSaveWarning } from 'app/icons/icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { NgClass } from '@angular/common'; +import { UpdatingResultComponent } from 'app/exercises/shared/result/updating-result.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-exam-exercise-overview-page', templateUrl: './exam-exercise-overview-page.component.html', styleUrls: ['./exam-exercise-overview-page.scss', '../../exam-navigation-sidebar/exam-navigation-sidebar.component.scss'], + imports: [TranslateDirective, FaIconComponent, NgbTooltip, NgClass, UpdatingResultComponent, ArtemisTranslatePipe], }) export class ExamExerciseOverviewPageComponent extends ExamPageComponent implements OnInit, OnChanges { + private examParticipationService = inject(ExamParticipationService); + @Input() studentExam: StudentExam; @Output() onPageChanged = new EventEmitter<{ overViewChange: boolean; exercise: Exercise; forceSave: boolean }>(); getIcon = getIcon; getIconTooltip = getIconTooltip; - readonly ExerciseButtonStatus = ExerciseButtonStatus; showResultWidth = 10; examExerciseOverviewItems: ExamExerciseOverviewItem[] = []; - constructor( - protected changeDetectorReference: ChangeDetectorRef, - private examParticipationService: ExamParticipationService, - ) { - super(changeDetectorReference); - } - ngOnInit() { this.studentExam.exercises?.forEach((exercise) => { const item = new ExamExerciseOverviewItem(); diff --git a/src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts b/src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts index 01e627a6941f..74fb67154b1e 100644 --- a/src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/exercise-save-button/exercise-save-button.component.ts @@ -1,7 +1,7 @@ import { Component, input, output } from '@angular/core'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { faFloppyDisk } from '@fortawesome/free-solid-svg-icons'; -import { facSaveSuccess } from '../../../../../content/icons/icons'; +import { facSaveSuccess } from 'app/icons/icons'; import { Submission } from 'app/entities/submission.model'; import { TranslateDirective } from 'app/shared/language/translate.directive'; @@ -9,7 +9,6 @@ import { TranslateDirective } from 'app/shared/language/translate.directive'; selector: 'jhi-exercise-save-button', templateUrl: './exercise-save-button.component.html', styleUrls: ['./exercise-save-button.component.scss'], - standalone: true, imports: [FaIconComponent, TranslateDirective], }) export class ExerciseSaveButtonComponent { diff --git a/src/main/webapp/app/exam/participate/exercises/file-upload/file-upload-exam-submission.component.ts b/src/main/webapp/app/exam/participate/exercises/file-upload/file-upload-exam-submission.component.ts index cec4402861c4..3f16c4b89c92 100644 --- a/src/main/webapp/app/exam/participate/exercises/file-upload/file-upload-exam-submission.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/file-upload/file-upload-exam-submission.component.ts @@ -1,16 +1,12 @@ -import { ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core'; -import { Location } from '@angular/common'; +import { Component, ElementRef, Input, OnInit, ViewChild, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; -import { ActivatedRoute } from '@angular/router'; import { AlertService } from 'app/core/util/alert.service'; import dayjs from 'dayjs/esm'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { FileUploadSubmissionService } from 'app/exercises/file-upload/participate/file-upload-submission.service'; -import { FileUploaderService } from 'app/shared/http/file-uploader.service'; import { MAX_SUBMISSION_FILE_SIZE } from 'app/shared/constants/input.constants'; import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { FileService } from 'app/shared/http/file.service'; -import { ResultService } from 'app/exercises/shared/result/result.service'; import { FileUploadSubmission } from 'app/entities/file-upload-submission.model'; import { ButtonType } from 'app/shared/components/button.component'; import { Result } from 'app/entities/result.model'; @@ -20,22 +16,40 @@ import { Submission } from 'app/entities/submission.model'; import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { SubmissionVersion } from 'app/entities/submission-version.model'; import { htmlForMarkdown } from 'app/shared/util/markdown.conversion.util'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-headers/included-in-score-badge.component'; +import { ResizeableContainerComponent } from 'app/shared/resizeable-container/resizeable-container.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ExamExerciseUpdateHighlighterComponent } from '../exam-exercise-update-highlighter/exam-exercise-update-highlighter.component'; +import { UpperCasePipe } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-file-upload-submission-exam', templateUrl: './file-upload-exam-submission.component.html', providers: [{ provide: ExamSubmissionComponent, useExisting: FileUploadExamSubmissionComponent }], - // change deactivation must be triggered manually + imports: [ + TranslateDirective, + IncludedInScoreBadgeComponent, + ResizeableContainerComponent, + FaIconComponent, + ExamExerciseUpdateHighlighterComponent, + UpperCasePipe, + ArtemisTranslatePipe, + ], }) export class FileUploadExamSubmissionComponent extends ExamSubmissionComponent implements OnInit { + private fileUploadSubmissionService = inject(FileUploadSubmissionService); + private alertService = inject(AlertService); + private translateService = inject(TranslateService); + private fileService = inject(FileService); + exerciseType = ExerciseType.FILE_UPLOAD; @ViewChild('fileInput', { static: false }) fileInput: ElementRef; - @Input() - studentSubmission: FileUploadSubmission; - @Input() - exercise: FileUploadExercise; + @Input() studentSubmission: FileUploadSubmission; + @Input() exercise: FileUploadExercise; problemStatementHtml: string; submittedFileName: string; @@ -52,20 +66,6 @@ export class FileUploadExamSubmissionComponent extends ExamSubmissionComponent i // Icons farListAlt = faListAlt; - constructor( - private route: ActivatedRoute, - private fileUploadSubmissionService: FileUploadSubmissionService, - private fileUploaderService: FileUploaderService, - private resultService: ResultService, - private alertService: AlertService, - private location: Location, - private translateService: TranslateService, - private fileService: FileService, - changeDetectorReference: ChangeDetectorRef, - ) { - super(changeDetectorReference); - } - /** * Initializes data for file upload editor */ diff --git a/src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts b/src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts index d16f4d56ff6d..fb5f8c062970 100644 --- a/src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/modeling/modeling-exam-submission.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, ViewChild, input, output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit, ViewChild, input, output } from '@angular/core'; import { UMLModel } from '@ls1intum/apollon'; import dayjs from 'dayjs/esm'; import { ModelingSubmission } from 'app/entities/modeling-submission.model'; @@ -10,6 +10,14 @@ import { Exercise, ExerciseType, IncludedInOverallScore } from 'app/entities/exe import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { SubmissionVersion } from 'app/entities/submission-version.model'; import { htmlForMarkdown } from 'app/shared/util/markdown.conversion.util'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-headers/included-in-score-badge.component'; +import { ExerciseSaveButtonComponent } from '../exercise-save-button/exercise-save-button.component'; +import { ResizeableContainerComponent } from 'app/shared/resizeable-container/resizeable-container.component'; +import { FullscreenComponent } from 'app/shared/fullscreen/fullscreen.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ExamExerciseUpdateHighlighterComponent } from '../exam-exercise-update-highlighter/exam-exercise-update-highlighter.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-modeling-submission-exam', @@ -18,20 +26,28 @@ import { htmlForMarkdown } from 'app/shared/util/markdown.conversion.util'; styleUrls: ['./modeling-exam-submission.component.scss'], // change deactivation must be triggered manually changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + TranslateDirective, + IncludedInScoreBadgeComponent, + ExerciseSaveButtonComponent, + ResizeableContainerComponent, + FullscreenComponent, + ModelingEditorComponent, + FaIconComponent, + ExamExerciseUpdateHighlighterComponent, + ArtemisTranslatePipe, + ], }) export class ModelingExamSubmissionComponent extends ExamSubmissionComponent implements OnInit { exerciseType = ExerciseType.MODELING; - @ViewChild(ModelingEditorComponent, { static: false }) - modelingEditor: ModelingEditorComponent; + @ViewChild(ModelingEditorComponent, { static: false }) modelingEditor: ModelingEditorComponent; // IMPORTANT: this reference must be contained in this.studentParticipation.submissions[0] otherwise the parent component will not be able to react to changes - @Input() - studentSubmission: ModelingSubmission; + @Input() studentSubmission: ModelingSubmission; problemStatementHtml: string; - @Input() - exercise: ModelingExercise; + @Input() exercise: ModelingExercise; umlModel: UMLModel; // input model for Apollon+ // explicitly needed to track if submission.isSynced is changed, otherwise component @@ -46,10 +62,6 @@ export class ModelingExamSubmissionComponent extends ExamSubmissionComponent imp // Icons protected readonly faListAlt = faListAlt; - constructor(changeDetectorReference: ChangeDetectorRef) { - super(changeDetectorReference); - } - ngOnInit(): void { // show submission answers in UI this.problemStatementHtml = htmlForMarkdown(this.exercise?.problemStatement); @@ -122,7 +134,7 @@ export class ModelingExamSubmissionComponent extends ExamSubmissionComponent imp } // eslint-disable-next-line @typescript-eslint/no-unused-vars - modelChanged(model: UMLModel) { + modelChanged(_model: UMLModel) { this.studentSubmission.isSynced = false; } diff --git a/src/main/webapp/app/exam/participate/exercises/programming/programming-exam-submission.component.ts b/src/main/webapp/app/exam/participate/exercises/programming/programming-exam-submission.component.ts index 7b18cda9e20a..0b9116699d02 100644 --- a/src/main/webapp/app/exam/participate/exercises/programming/programming-exam-submission.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/programming/programming-exam-submission.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, Input, OnChanges, OnInit, ViewChild } from '@angular/core'; +import { Component, Input, OnChanges, OnInit, ViewChild, inject } from '@angular/core'; import { Submission } from 'app/entities/submission.model'; import { ExamSubmissionComponent } from 'app/exam/participate/exercises/exam-submission.component'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; @@ -19,6 +19,13 @@ import { CodeEditorRepositoryService, } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; import { SubmissionVersion } from 'app/entities/submission-version.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-headers/included-in-score-badge.component'; +import { ProgrammingSubmissionPolicyStatusComponent } from 'app/exercises/programming/participate/programming-submission-policy-status'; +import { ExerciseDetailsStudentActionsComponent } from 'app/overview/exercise-details/exercise-details-student-actions.component'; +import { CodeEditorRepositoryIsLockedComponent } from 'app/exercises/programming/shared/code-editor/layout/code-editor-repository-is-locked.component'; +import { UpdatingResultComponent } from 'app/exercises/shared/result/updating-result.component'; +import { ProgrammingExerciseStudentTriggerBuildButtonComponent } from 'app/exercises/programming/shared/actions/programming-exercise-student-trigger-build-button.component'; @Component({ selector: 'jhi-programming-submission-exam', @@ -32,20 +39,30 @@ import { SubmissionVersion } from 'app/entities/submission-version.model'; CodeEditorRepositoryService, ], styleUrls: ['./programming-exam-submission.component.scss'], + imports: [ + TranslateDirective, + IncludedInScoreBadgeComponent, + CodeEditorContainerComponent, + ProgrammingSubmissionPolicyStatusComponent, + ExerciseDetailsStudentActionsComponent, + CodeEditorRepositoryIsLockedComponent, + UpdatingResultComponent, + ProgrammingExerciseStudentTriggerBuildButtonComponent, + ProgrammingExerciseInstructionComponent, + ], }) export class ProgrammingExamSubmissionComponent extends ExamSubmissionComponent implements OnChanges, OnInit { + private domainService = inject(DomainService); + exerciseType = ExerciseType.PROGRAMMING; @ViewChild(CodeEditorContainerComponent, { static: false }) codeEditorContainer: CodeEditorContainerComponent; @ViewChild(ProgrammingExerciseInstructionComponent, { static: false }) instructions: ProgrammingExerciseInstructionComponent; // IMPORTANT: this reference must be activeExercise.studentParticipation[0] otherwise the parent component will not be able to react to change - @Input() - studentParticipation: ProgrammingExerciseStudentParticipation; - @Input() - exercise: ProgrammingExercise; - @Input() - courseId: number; + @Input() studentParticipation: ProgrammingExerciseStudentParticipation; + @Input() exercise: ProgrammingExercise; + @Input() courseId: number; showEditorInstructions = true; hasSubmittedOnce = false; @@ -75,13 +92,6 @@ export class ProgrammingExamSubmissionComponent extends ExamSubmissionComponent readonly ButtonType = ButtonType; readonly ButtonSize = ButtonSize; - constructor( - private domainService: DomainService, - changeDetectorReference: ChangeDetectorRef, - ) { - super(changeDetectorReference); - } - /** * On init set up the route param subscription. * Will load the participation according to participation Id with the latest result and result details. @@ -91,7 +101,7 @@ export class ProgrammingExamSubmissionComponent extends ExamSubmissionComponent this.setSubmissionCountAndLockIfNeeded(); } - ngOnChanges(): void { + ngOnChanges() { this.setSubmissionCountAndLockIfNeeded(); } diff --git a/src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.ts b/src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.ts index 26aa88a762d5..deff24ba6ecc 100644 --- a/src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/quiz/quiz-exam-submission.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, Input, OnInit, QueryList, ViewChildren, output } from '@angular/core'; +import { Component, Input, OnInit, QueryList, ViewChildren, inject, output } from '@angular/core'; import { Exercise, ExerciseType, IncludedInOverallScore } from 'app/entities/exercise.model'; import { AbstractQuizSubmission } from 'app/entities/quiz/abstract-quiz-exam-submission.model'; import { AnswerOption } from 'app/entities/quiz/answer-option.model'; @@ -20,14 +20,33 @@ import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; import { ArtemisQuizService } from 'app/shared/quiz/quiz.service'; import { cloneDeep } from 'lodash-es'; import * as smoothscroll from 'smoothscroll-polyfill'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-headers/included-in-score-badge.component'; +import { ExerciseSaveButtonComponent } from '../exercise-save-button/exercise-save-button.component'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { NgClass } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-quiz-submission-exam', templateUrl: './quiz-exam-submission.component.html', providers: [{ provide: ExamSubmissionComponent, useExisting: QuizExamSubmissionComponent }], styleUrls: ['./quiz-exam-submission.component.scss'], + imports: [ + TranslateDirective, + IncludedInScoreBadgeComponent, + ExerciseSaveButtonComponent, + NgbTooltip, + NgClass, + MultipleChoiceQuestionComponent, + DragAndDropQuestionComponent, + ShortAnswerQuestionComponent, + ArtemisTranslatePipe, + ], }) export class QuizExamSubmissionComponent extends ExamSubmissionComponent implements OnInit { + private quizService = inject(ArtemisQuizService); + exerciseType = ExerciseType.QUIZ; // make constants available to html for comparison @@ -38,14 +57,9 @@ export class QuizExamSubmissionComponent extends ExamSubmissionComponent impleme readonly ButtonType = ButtonType; readonly IncludedInOverallScore = IncludedInOverallScore; - @ViewChildren(MultipleChoiceQuestionComponent) - mcQuestionComponents: QueryList; - - @ViewChildren(DragAndDropQuestionComponent) - dndQuestionComponents: QueryList; - - @ViewChildren(ShortAnswerQuestionComponent) - shortAnswerQuestionComponents: QueryList; + @ViewChildren(MultipleChoiceQuestionComponent) mcQuestionComponents: QueryList; + @ViewChildren(DragAndDropQuestionComponent) dndQuestionComponents: QueryList; + @ViewChildren(ShortAnswerQuestionComponent) shortAnswerQuestionComponents: QueryList; // IMPORTANT: this reference must be contained in this.studentParticipation.submissions[0] otherwise the parent component will not be able to react to changes @Input() studentSubmission: AbstractQuizSubmission; @@ -59,15 +73,8 @@ export class QuizExamSubmissionComponent extends ExamSubmissionComponent impleme dragAndDropMappings = new Map(); shortAnswerSubmittedTexts = new Map(); - constructor( - private quizService: ArtemisQuizService, - changeDetectorReference: ChangeDetectorRef, - ) { - super(changeDetectorReference); - smoothscroll.polyfill(); - } - ngOnInit(): void { + smoothscroll.polyfill(); this.initQuiz(); this.updateViewFromSubmission(); } diff --git a/src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts b/src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts index cb2dc3c0fd51..317eae54f8e4 100644 --- a/src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts +++ b/src/main/webapp/app/exam/participate/exercises/text/text-exam-submission.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, Input, OnInit, output } from '@angular/core'; +import { Component, Input, OnInit, inject, output } from '@angular/core'; import { TextEditorService } from 'app/exercises/text/participate/text-editor.service'; import { Subject } from 'rxjs'; import { TextSubmission } from 'app/entities/text/text-submission.model'; @@ -10,21 +10,41 @@ import { faListAlt } from '@fortawesome/free-solid-svg-icons'; import { MAX_SUBMISSION_TEXT_LENGTH } from 'app/shared/constants/input.constants'; import { SubmissionVersion } from 'app/entities/submission-version.model'; import { htmlForMarkdown } from 'app/shared/util/markdown.conversion.util'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { IncludedInScoreBadgeComponent } from 'app/exercises/shared/exercise-headers/included-in-score-badge.component'; +import { ExerciseSaveButtonComponent } from '../exercise-save-button/exercise-save-button.component'; +import { ResizeableContainerComponent } from 'app/shared/resizeable-container/resizeable-container.component'; +import { FormsModule } from '@angular/forms'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ExamExerciseUpdateHighlighterComponent } from '../exam-exercise-update-highlighter/exam-exercise-update-highlighter.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { onTextEditorTab } from 'app/utils/text.utils'; @Component({ selector: 'jhi-text-editor-exam', templateUrl: './text-exam-submission.component.html', providers: [{ provide: ExamSubmissionComponent, useExisting: TextExamSubmissionComponent }], styleUrls: ['./text-exam-submission.component.scss'], + imports: [ + TranslateDirective, + IncludedInScoreBadgeComponent, + ExerciseSaveButtonComponent, + ResizeableContainerComponent, + FormsModule, + FaIconComponent, + ExamExerciseUpdateHighlighterComponent, + ArtemisTranslatePipe, + ], }) export class TextExamSubmissionComponent extends ExamSubmissionComponent implements OnInit { + private textService = inject(TextEditorService); + private stringCountService = inject(StringCountService); + exerciseType = ExerciseType.TEXT; // IMPORTANT: this reference must be contained in this.studentParticipation.submissions[0] otherwise the parent component will not be able to react to changes - @Input() - studentSubmission: TextSubmission; - @Input() - exercise: Exercise; + @Input() studentSubmission: TextSubmission; + @Input() exercise: Exercise; saveCurrentExercise = output(); @@ -39,13 +59,8 @@ export class TextExamSubmissionComponent extends ExamSubmissionComponent impleme // Icons protected readonly faListAlt = faListAlt; - constructor( - private textService: TextEditorService, - private stringCountService: StringCountService, - changeDetectorReference: ChangeDetectorRef, - ) { - super(changeDetectorReference); - } + // used in the html template + protected readonly onTextEditorTab = onTextEditorTab; ngOnInit(): void { // show submission answers in UI @@ -95,16 +110,6 @@ export class TextExamSubmissionComponent extends ExamSubmissionComponent impleme return this.stringCountService.countCharacters(this.answer); } - onTextEditorTab(editor: HTMLTextAreaElement, event: Event) { - event.preventDefault(); - const value = editor.value; - const start = editor.selectionStart; - const end = editor.selectionEnd; - - editor.value = value.substring(0, start) + '\t' + value.substring(end); - editor.selectionStart = editor.selectionEnd = start + 1; - } - onTextEditorInput(event: Event) { this.studentSubmission.isSynced = false; this.textEditorInput.next((event.target).value); diff --git a/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.html b/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.html index d6b4a6cd2c74..ec867ce353cd 100644 --- a/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.html +++ b/src/main/webapp/app/exam/participate/general-information/exam-general-information.component.html @@ -119,7 +119,7 @@

    +

           {{ programmingExercise.maxPoints }} {{ programmingExercise.bonusPoints }} {{ exerciseService.isIncludedInScore(programmingExercise) }} + + +
    @if (programmingExercise.templateParticipation && programmingExercise.templateParticipation.repositoryUri) { @@ -128,7 +131,9 @@
    + + + @if (programmingExercise.templateParticipation?.buildPlanId) { Template } @@ -159,7 +164,9 @@ + + +
    @if (programmingExercise.teamMode && programmingExercise.isAtLeastTutor) { diff --git a/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts b/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts index 3a33c7b88f83..57220812410c 100644 --- a/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts +++ b/src/main/webapp/app/exercises/programming/manage/programming-exercise.component.ts @@ -1,5 +1,6 @@ -import { Component, ContentChild, Input, OnDestroy, OnInit, TemplateRef, inject } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; +import { ExerciseScoresExportButtonComponent } from 'app/exercises/shared/exercise-scores/exercise-scores-export-button.component'; import { merge } from 'rxjs'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseInstructorRepositoryType, ProgrammingExerciseService } from './services/programming-exercise.service'; @@ -22,7 +23,6 @@ import { faCheckDouble, faDownload, faFileSignature, - faLightbulb, faListAlt, faPencilAlt, faPlus, @@ -36,10 +36,47 @@ import { import { CourseExerciseService } from 'app/exercises/shared/course-exercises/course-exercise.service'; import { downloadZipFileFromResponse } from 'app/shared/util/download.util'; import { PROFILE_LOCALCI, PROFILE_LOCALVC, PROFILE_THEIA } from 'app/app.constants'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { FormsModule } from '@angular/forms'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { RouterLink } from '@angular/router'; +import { ProgrammingExerciseGradingDirtyWarningComponent } from './grading/programming-exercise-grading-dirty-warning.component'; +import { ProgrammingExerciseInstructorStatusComponent } from './status/programming-exercise-instructor-status.component'; +import { ExerciseCategoriesComponent } from 'app/shared/exercise-categories/exercise-categories.component'; +import { FeatureToggleLinkDirective } from 'app/shared/feature-toggle/feature-toggle-link.directive'; +import { ProgrammingExerciseResetButtonDirective } from './reset/programming-exercise-reset-button.directive'; +import { FeatureToggleDirective } from 'app/shared/feature-toggle/feature-toggle.directive'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ProgrammingAssessmentRepoExportButtonComponent } from '../assess/repo-export/programming-assessment-repo-export-button.component'; +import { SlicePipe } from '@angular/common'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-programming-exercise', templateUrl: './programming-exercise.component.html', + imports: [ + SortDirective, + FormsModule, + SortByDirective, + TranslateDirective, + FaIconComponent, + RouterLink, + ProgrammingExerciseGradingDirtyWarningComponent, + ProgrammingExerciseInstructorStatusComponent, + ExerciseCategoriesComponent, + FeatureToggleLinkDirective, + ProgrammingExerciseResetButtonDirective, + FeatureToggleDirective, + DeleteButtonDirective, + ProgrammingAssessmentRepoExportButtonComponent, + ExerciseScoresExportButtonComponent, + SlicePipe, + ArtemisDatePipe, + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // ExtensionPointDirective, + ], }) export class ProgrammingExerciseComponent extends ExerciseComponent implements OnInit, OnDestroy { protected exerciseService = inject(ExerciseService); @@ -63,8 +100,9 @@ export class ProgrammingExerciseComponent extends ExerciseComponent implements O onlineIdeEnabled = false; // extension points, see shared/extension-point - @ContentChild('overrideRepositoryAndBuildPlan') overrideRepositoryAndBuildPlan: TemplateRef; - @ContentChild('overrideButtons') overrideButtons: TemplateRef; + // TODO: the extension point for Orion does not work with Angular 19, we need to find a different solution + // @ContentChild('overrideRepositoryAndBuildPlan') overrideRepositoryAndBuildPlan: TemplateRef; + // @ContentChild('overrideButtons') overrideButtons: TemplateRef; private buildPlanLinkTemplate: string; // Icons @@ -79,7 +117,6 @@ export class ProgrammingExerciseComponent extends ExerciseComponent implements O faTable = faTable; faTrash = faTrash; faListAlt = faListAlt; - faLightbulb = faLightbulb; faPencilAlt = faPencilAlt; faFileSignature = faFileSignature; @@ -135,7 +172,7 @@ export class ProgrammingExerciseComponent extends ExerciseComponent implements O this.emitFilteredExerciseCount(this.filteredProgrammingExercises.length); } - trackId(index: number, item: ProgrammingExercise) { + trackId(_index: number, item: ProgrammingExercise) { return item.id; } diff --git a/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-button.directive.ts b/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-button.directive.ts index f249959c63b5..dc1a02192c7c 100644 --- a/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-button.directive.ts +++ b/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-button.directive.ts @@ -1,19 +1,15 @@ -import { Directive, ElementRef, HostListener, Input, OnInit, Renderer2 } from '@angular/core'; +import { Directive, ElementRef, HostListener, Input, OnInit, Renderer2, inject } from '@angular/core'; import { ProgrammingExerciseResetDialogComponent } from 'app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -@Directive({ - selector: '[jhiProgrammingExerciseResetButton]', -}) +@Directive({ selector: '[jhiProgrammingExerciseResetButton]' }) export class ProgrammingExerciseResetButtonDirective implements OnInit { - @Input() programmingExercise: ProgrammingExercise; + private modalService = inject(NgbModal); + private renderer = inject(Renderer2); + private elementRef = inject(ElementRef); - constructor( - private modalService: NgbModal, - private renderer: Renderer2, - private elementRef: ElementRef, - ) {} + @Input() programmingExercise: ProgrammingExercise; ngOnInit() { this.renderer.addClass(this.elementRef.nativeElement, 'btn'); diff --git a/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component.html b/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component.html index aab1fee57223..677358ac3321 100644 --- a/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component.html +++ b/src/main/webapp/app/exercises/programming/manage/reset/programming-exercise-reset-dialog.component.html @@ -118,8 +118,8 @@
    `, + imports: [FormsModule, TranslateDirective, FaIconComponent], }) export class ProgrammingExerciseInstructorTriggerAllDialogComponent { + private activeModal = inject(NgbActiveModal); + @Input() exerciseId: number; @Input() dueDatePassed: boolean; @@ -119,8 +125,6 @@ export class ProgrammingExerciseInstructorTriggerAllDialogComponent { faBan = faBan; faTimes = faTimes; - constructor(private activeModal: NgbActiveModal) {} - cancel() { this.activeModal.dismiss('cancel'); } diff --git a/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-build-button.component.ts b/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-build-button.component.ts index 1431ad0bfe38..14ff2154ed02 100644 --- a/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-build-button.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/actions/programming-exercise-trigger-build-button.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnChanges, OnDestroy, SimpleChanges } from '@angular/core'; +import { Component, Input, OnChanges, OnDestroy, SimpleChanges, inject } from '@angular/core'; import { filter, tap } from 'rxjs/operators'; import { Subscription } from 'rxjs'; import { head, orderBy } from 'lodash-es'; @@ -11,7 +11,6 @@ import { Result } from 'app/entities/result.model'; import { FeatureToggle } from 'app/shared/feature-toggle/feature-toggle.service'; import { SubmissionType } from 'app/entities/submission.model'; import { ProgrammingExercise } from 'app/entities/programming/programming-exercise.model'; -import { AlertService } from 'app/core/util/alert.service'; import { hasParticipationChanged } from 'app/exercises/shared/participation/participation.utils'; import { isManualResult } from 'app/exercises/shared/result/result.utils'; @@ -20,11 +19,16 @@ import { isManualResult } from 'app/exercises/shared/result/result.utils'; * The participation given as input needs to have the results attached as this component checks if there is at least one result. * If there is no result, the button is disabled because this would mean that the student has not made a commit yet. */ -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class ProgrammingExerciseTriggerBuildButtonComponent implements OnChanges, OnDestroy { FeatureToggle = FeatureToggle; ButtonType = ButtonType; + private submissionService = inject(ProgrammingSubmissionService); + private participationWebsocketService = inject(ParticipationWebsocketService); + @Input() exercise: ProgrammingExercise; @Input() participation: Participation; @Input() btnSize = ButtonSize.SMALL; @@ -44,12 +48,6 @@ export abstract class ProgrammingExerciseTriggerBuildButtonComponent implements // True if the student triggers. false if an instructor triggers it protected personalParticipation: boolean; - protected constructor( - protected submissionService: ProgrammingSubmissionService, - protected participationWebsocketService: ParticipationWebsocketService, - protected alertService: AlertService, - ) {} - /** * Check if the participation has changed, if so set up the websocket connections. * diff --git a/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component.ts b/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component.ts index d1932dc53ff3..d5233d052bed 100644 --- a/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-build-plan-checkout-directories.component.ts @@ -6,7 +6,6 @@ import { AuxiliaryRepository } from 'app/entities/programming/programming-exerci selector: 'jhi-programming-exercise-build-plan-checkout-directories', templateUrl: './programming-exercise-build-plan-checkout-directories.component.html', styleUrls: ['../../manage/programming-exercise-form.scss'], - standalone: true, }) export class ProgrammingExerciseBuildPlanCheckoutDirectoriesComponent { @Input() checkoutDirectories?: BuildPlanCheckoutDirectoriesDTO; diff --git a/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-edit-checkout-directories/programming-exercise-edit-checkout-directories.component.ts b/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-edit-checkout-directories/programming-exercise-edit-checkout-directories.component.ts index 460a48c84aea..751db4de032c 100644 --- a/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-edit-checkout-directories/programming-exercise-edit-checkout-directories.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-edit-checkout-directories/programming-exercise-edit-checkout-directories.component.ts @@ -8,7 +8,6 @@ import { NgModel } from '@angular/forms'; @Component({ selector: 'jhi-programming-exercise-edit-checkout-directories', - standalone: true, imports: [ArtemisSharedComponentModule, ArtemisSharedCommonModule], templateUrl: './programming-exercise-edit-checkout-directories.component.html', styleUrls: ['../../../manage/programming-exercise-form.scss'], @@ -25,11 +24,11 @@ export class ProgrammingExerciseEditCheckoutDirectoriesComponent { testCheckoutPath: string; solutionCheckoutPath: string; - isAssigmentRepositoryEditable: boolean = false; - isTestRepositoryEditable: boolean = false; - isSolutionRepositoryEditable: boolean = false; + isAssigmentRepositoryEditable = false; + isTestRepositoryEditable = false; + isSolutionRepositoryEditable = false; - formValid: boolean = true; + formValid = true; formValidChanges = new Subject(); field_assignmentRepositoryCheckoutPath = viewChild('field_assignmentRepositoryCheckoutPath'); diff --git a/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component.ts b/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component.ts index ae0a3a091ca6..86ce68ae1b09 100644 --- a/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/build-details/programming-exercise-repository-and-build-plan-details.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, inject } from '@angular/core'; import { getCourseFromExercise } from 'app/entities/exercise.model'; import type { ProgrammingExercise, ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; import { ProgrammingExerciseBuildConfig } from 'app/entities/programming/programming-exercise-build.config'; @@ -14,21 +14,20 @@ import { BuildPlanCheckoutDirectoriesDTO } from 'app/entities/programming/build- selector: 'jhi-programming-exercise-repository-and-build-plan-details', templateUrl: './programming-exercise-repository-and-build-plan-details.component.html', styleUrls: ['../../manage/programming-exercise-form.scss'], - standalone: true, imports: [ArtemisSharedComponentModule, ArtemisSharedCommonModule, ProgrammingExerciseBuildPlanCheckoutDirectoriesComponent], }) export class ProgrammingExerciseRepositoryAndBuildPlanDetailsComponent implements OnInit, OnChanges, OnDestroy { + private programmingExerciseService = inject(ProgrammingExerciseService); + @Input() programmingExercise: ProgrammingExercise; @Input() programmingExerciseBuildConfig?: ProgrammingExerciseBuildConfig; @Input() programmingLanguage?: ProgrammingLanguage; @Input() isLocal: boolean; - @Input() checkoutSolutionRepository?: boolean = true; + @Input() checkoutSolutionRepository = true; @Input() isCreateOrEdit = false; @Input() isEditMode = false; @Output() submissionBuildPlanEvent = new EventEmitter(); - constructor(private programmingExerciseService: ProgrammingExerciseService) {} - checkoutDirectorySubscription?: Subscription; courseShortName?: string; diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-actions.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-actions.component.ts index 2eafc4badde4..aa6558311aa9 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-actions.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-actions.component.ts @@ -1,6 +1,6 @@ -import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, input } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, inject, input } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { catchError, switchMap, tap } from 'rxjs/operators'; import { Observable, Subscription, of, throwError } from 'rxjs'; import { isEmpty as _isEmpty } from 'lodash-es'; @@ -15,12 +15,24 @@ import { AUTOSAVE_CHECK_INTERVAL, AUTOSAVE_EXERCISE_INTERVAL } from 'app/shared/ import { faCircleNotch, faSync, faTimes } from '@fortawesome/free-solid-svg-icons'; import { faPlayCircle } from '@fortawesome/free-regular-svg-icons'; import { Participation } from 'app/entities/participation/participation.model'; +import { RequestFeedbackButtonComponent } from 'app/overview/exercise-details/request-feedback-button/request-feedback-button.component'; +import { FeatureToggleDirective } from 'app/shared/feature-toggle/feature-toggle.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-code-editor-actions', templateUrl: './code-editor-actions.component.html', + imports: [RequestFeedbackButtonComponent, FeatureToggleDirective, NgbTooltip, FaIconComponent, TranslateDirective, ArtemisTranslatePipe], }) export class CodeEditorActionsComponent implements OnInit, OnDestroy, OnChanges { + private repositoryService = inject(CodeEditorRepositoryService); + private repositoryFileService = inject(CodeEditorRepositoryFileService); + private conflictService = inject(CodeEditorConflictStateService); + private modalService = inject(NgbModal); + private submissionService = inject(CodeEditorSubmissionService); + CommitState = CommitState; EditorState = EditorState; FeatureToggle = FeatureToggle; @@ -72,14 +84,6 @@ export class CodeEditorActionsComponent implements OnInit, OnDestroy, OnChanges this.editorStateChange.emit(editorState); } - constructor( - private repositoryService: CodeEditorRepositoryService, - private repositoryFileService: CodeEditorRepositoryFileService, - private conflictService: CodeEditorConflictStateService, - private modalService: NgbModal, - private submissionService: CodeEditorSubmissionService, - ) {} - ngOnInit(): void { this.conflictStateSubscription = this.conflictService.subscribeConflictState().subscribe((gitConflictState: GitConflictState) => { // When the conflict is encountered when opening the code-editor, setting the commitState here could cause an uncheckedException. @@ -171,7 +175,7 @@ export class CodeEditorActionsComponent implements OnInit, OnDestroy, OnChanges } /** - * @function saveFiles + * @param andCommit whether the saved changed in the files should be committed or not * @desc Saves all files that have unsaved changes in the editor. */ saveChangedFiles(andCommit = false): Observable { diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-confirm-refresh-modal.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-confirm-refresh-modal.component.ts index c74bd5cee679..de69a79d85c8 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-confirm-refresh-modal.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-confirm-refresh-modal.component.ts @@ -1,27 +1,29 @@ -import { Component, EventEmitter } from '@angular/core'; +import { Component, EventEmitter, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { CodeEditorRepositoryFileService, CodeEditorRepositoryService } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; import { CodeEditorConflictStateService } from 'app/exercises/programming/shared/code-editor/service/code-editor-conflict-state.service'; import { faBan, faExclamationTriangle, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-code-editor-confirm-refresh-modal', templateUrl: './code-editor-confirm-refresh-modal.component.html', styleUrls: ['./code-editor-resolve-conflict-modal.scss'], providers: [CodeEditorRepositoryFileService], + imports: [FormsModule, TranslateDirective, FaIconComponent], }) export class CodeEditorConfirmRefreshModalComponent { + activeModal = inject(NgbActiveModal); + private repositoryService = inject(CodeEditorRepositoryService); + private conflictService = inject(CodeEditorConflictStateService); + // Icons faExclamationTriangle = faExclamationTriangle; faBan = faBan; faTimes = faTimes; - constructor( - public activeModal: NgbActiveModal, - private repositoryService: CodeEditorRepositoryService, - private conflictService: CodeEditorConflictStateService, - ) {} - shouldRefresh: EventEmitter = new EventEmitter(); /** diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-resolve-conflict-modal.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-resolve-conflict-modal.component.ts index 013b41adfea5..68934a3d9dde 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-resolve-conflict-modal.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/actions/code-editor-resolve-conflict-modal.component.ts @@ -1,20 +1,24 @@ -import { Component, EventEmitter } from '@angular/core'; +import { Component, EventEmitter, inject } from '@angular/core'; import { faBan, faExclamationTriangle, faTimes } from '@fortawesome/free-solid-svg-icons'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-code-editor-resolve-conflict-modal', templateUrl: './code-editor-resolve-conflict-modal.component.html', styleUrls: ['./code-editor-resolve-conflict-modal.scss'], + imports: [FormsModule, TranslateDirective, FaIconComponent], }) export class CodeEditorResolveConflictModalComponent { + activeModal = inject(NgbActiveModal); + // Icons faBan = faBan; faTimes = faTimes; faExclamationTriangle = faExclamationTriangle; - constructor(public activeModal: NgbActiveModal) {} - shouldReset: EventEmitter = new EventEmitter(); /** diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/build-output/code-editor-build-output.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/build-output/code-editor-build-output.component.ts index 1c7535cbc9d6..d055b35bb665 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/build-output/code-editor-build-output.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/build-output/code-editor-build-output.component.ts @@ -1,5 +1,5 @@ import { ParticipationWebsocketService } from 'app/overview/participation-websocket.service'; -import { AfterViewInit, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; +import { AfterViewInit, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, inject } from '@angular/core'; import { Observable, Subscription, of } from 'rxjs'; import { catchError, filter, map, switchMap, tap } from 'rxjs/operators'; import { BuildLogEntry, BuildLogEntryArray } from 'app/entities/programming/build-log.model'; @@ -19,13 +19,22 @@ import { faChevronDown, faCircleNotch, faTerminal } from '@fortawesome/free-soli import { hasParticipationChanged } from 'app/exercises/shared/participation/participation.utils'; import { AssessmentType } from 'app/entities/assessment-type.model'; import { Annotation } from '../monaco/code-editor-monaco.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-code-editor-build-output', templateUrl: './code-editor-build-output.component.html', styleUrls: ['./code-editor-build-output.scss'], + imports: [FaIconComponent, TranslateDirective, ArtemisDatePipe], }) export class CodeEditorBuildOutputComponent implements AfterViewInit, OnInit, OnChanges, OnDestroy { + private buildLogService = inject(CodeEditorBuildLogService); + private resultService = inject(ResultService); + private participationWebsocketService = inject(ParticipationWebsocketService); + private submissionService = inject(CodeEditorSubmissionService); + @Input() participation: Participation; @@ -52,13 +61,6 @@ export class CodeEditorBuildOutputComponent implements AfterViewInit, OnInit, On faCircleNotch = faCircleNotch; faTerminal = faTerminal; - constructor( - private buildLogService: CodeEditorBuildLogService, - private resultService: ResultService, - private participationWebsocketService: ParticipationWebsocketService, - private submissionService: CodeEditorSubmissionService, - ) {} - ngOnInit(): void { this.setupSubmissionWebsocket(); } diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/code-editor.module.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/code-editor.module.ts index a7d5af4a1cfc..9b42929ee85a 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/code-editor.module.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/code-editor.module.ts @@ -13,7 +13,7 @@ import { CodeEditorFileBrowserCreateNodeComponent } from 'app/exercises/programm import { CodeEditorFileBrowserFileComponent } from 'app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-file.component'; import { CodeEditorFileBrowserComponent } from 'app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser.component'; import { CodeEditorStatusComponent } from 'app/exercises/programming/shared/code-editor/status/code-editor-status.component'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { CodeEditorConfirmRefreshModalComponent } from 'app/exercises/programming/shared/code-editor/actions/code-editor-confirm-refresh-modal.component'; import { CodeEditorContainerComponent } from 'app/exercises/programming/shared/code-editor/container/code-editor-container.component'; import { ArtemisProgrammingManualAssessmentModule } from 'app/exercises/programming/assess/programming-manual-assessment.module'; @@ -29,7 +29,6 @@ import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.co imports: [ NgbModule, ArtemisSharedModule, - FeatureToggleModule, TreeviewModule.forRoot(), ArtemisProgrammingExerciseInstructionsEditorModule, ArtemisProgrammingManualAssessmentModule, @@ -37,8 +36,6 @@ import { MonacoEditorComponent } from 'app/shared/monaco-editor/monaco-editor.co ArtemisSharedComponentModule, RequestFeedbackButtonComponent, CodeEditorMonacoComponent, - ], - declarations: [ CodeEditorGridComponent, CodeEditorRepositoryIsLockedComponent, CodeEditorFileBrowserComponent, diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/container/code-editor-container.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/container/code-editor-container.component.ts index b2b81406b23a..30e1e4ed83e4 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/container/code-editor-container.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/container/code-editor-container.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewChild, inject } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { isEmpty as _isEmpty, fromPairs, toPairs, uniq } from 'lodash-es'; import { CodeEditorFileService } from 'app/exercises/programming/shared/code-editor/service/code-editor-file.service'; @@ -24,6 +24,7 @@ import { Feedback } from 'app/entities/feedback.model'; import { Course } from 'app/entities/course.model'; import { ConnectionError } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; import { Annotation, CodeEditorMonacoComponent } from 'app/exercises/programming/shared/code-editor/monaco/code-editor-monaco.component'; +import { KeysPipe } from 'app/shared/pipes/keys.pipe'; export enum CollapsableCodeEditorElement { FileBrowser, @@ -35,8 +36,21 @@ export enum CollapsableCodeEditorElement { selector: 'jhi-code-editor-container', templateUrl: './code-editor-container.component.html', styleUrls: ['./code-editor-container.component.scss'], + imports: [ + CodeEditorGridComponent, + CodeEditorActionsComponent, + CodeEditorFileBrowserComponent, + CodeEditorMonacoComponent, + CodeEditorInstructionsComponent, + CodeEditorBuildOutputComponent, + KeysPipe, + ], }) export class CodeEditorContainerComponent implements OnChanges { + private translateService = inject(TranslateService); + private alertService = inject(AlertService); + private fileService = inject(CodeEditorFileService); + readonly CommitState = CommitState; readonly EditorState = EditorState; readonly CollapsableCodeEditorElement = CollapsableCodeEditorElement; @@ -107,11 +121,7 @@ export class CodeEditorContainerComponent implements OnChanges { errorFiles: string[] = []; annotations: Array = []; - constructor( - private translateService: TranslateService, - private alertService: AlertService, - private fileService: CodeEditorFileService, - ) { + constructor() { this.initializeProperties(); } diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-badge.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-badge.component.ts index ea851defda95..9ec1808cc524 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-badge.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-badge.component.ts @@ -1,20 +1,23 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { IconDefinition, faLightbulb } from '@fortawesome/free-solid-svg-icons'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { FileBadge, FileBadgeType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-file-browser-badge', templateUrl: './code-editor-file-browser-badge.component.html', styleUrls: ['./code-editor-file-browser-badge.component.scss'], providers: [NgbModal], + imports: [NgbTooltip, FaIconComponent], }) export class CodeEditorFileBrowserBadgeComponent { - @Input() badge: FileBadge; - @Input() onColoredBackground: boolean = false; // Only slightly darken the background and use white text + private translateService = inject(TranslateService); - constructor(private translateService: TranslateService) {} + @Input() badge: FileBadge; + @Input() onColoredBackground = false; + // Only slightly darken the background and use white text get tooltip(): string | undefined { switch (this.badge.type) { diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-create-node.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-create-node.component.ts index c90d2c4272bc..5b2dbbd20a8c 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-create-node.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-create-node.component.ts @@ -2,12 +2,14 @@ import { AfterViewInit, Component, ElementRef, EventEmitter, Input, Output, View import { faFile, faFolder } from '@fortawesome/free-solid-svg-icons'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { FileType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-code-editor-file-browser-create-node', templateUrl: './code-editor-file-browser-create-node.component.html', styleUrls: ['./code-editor-file-browser-create-node.component.scss'], providers: [NgbModal], + imports: [FaIconComponent], }) export class CodeEditorFileBrowserCreateNodeComponent implements AfterViewInit { FileType = FileType; diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-delete.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-delete.ts index e393b4b81bee..35a5f759508c 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-delete.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-delete.ts @@ -1,18 +1,26 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, Input, OnInit, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { DeleteFileChange, FileType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { CodeEditorRepositoryFileService } from 'app/exercises/programming/shared/code-editor/service/code-editor-repository.service'; import { IFileDeleteDelegate } from 'app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-on-file-delete-delegate'; import { captureException } from '@sentry/angular'; import { faBan, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; // Modal -> Delete repository file @Component({ selector: 'jhi-code-editor-file-browser-delete', templateUrl: './code-editor-file-browser-delete.component.html', providers: [CodeEditorRepositoryFileService], + imports: [FormsModule, TranslateDirective, FaIconComponent, ArtemisTranslatePipe], }) export class CodeEditorFileBrowserDeleteComponent implements OnInit { + activeModal = inject(NgbActiveModal); + private repositoryFileService = inject(CodeEditorRepositoryFileService); + @Input() fileNameToDelete: string; @Input() parent: IFileDeleteDelegate; @Input() fileType: FileType; @@ -23,11 +31,6 @@ export class CodeEditorFileBrowserDeleteComponent implements OnInit { faBan = faBan; faTrashAlt = faTrashAlt; - constructor( - public activeModal: NgbActiveModal, - private repositoryFileService: CodeEditorRepositoryFileService, - ) {} - /** * @function ngOnInit * @desc Initializes variables diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-file.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-file.component.ts index b5738903dd33..ac3878a170d4 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-file.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-file.component.ts @@ -3,11 +3,16 @@ import { faEdit, faFile, faTrash } from '@fortawesome/free-solid-svg-icons'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { CodeEditorFileBrowserNodeComponent } from 'app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-node.component'; import { FileBadge } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass } from '@angular/common'; +import { CodeEditorFileBrowserBadgeComponent } from './code-editor-file-browser-badge.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-code-editor-file-browser-file', templateUrl: './code-editor-file-browser-file.component.html', providers: [NgbModal], + imports: [FaIconComponent, NgClass, CodeEditorFileBrowserBadgeComponent, ArtemisTranslatePipe], }) export class CodeEditorFileBrowserFileComponent extends CodeEditorFileBrowserNodeComponent { @Input() disableActions: boolean; diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-folder.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-folder.component.ts index d1693b244d82..bc824e76f760 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-folder.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-folder.component.ts @@ -4,11 +4,15 @@ import { FileBadge, FileType } from 'app/exercises/programming/shared/code-edito import { CodeEditorFileBrowserNodeComponent } from 'app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-node.component'; import { faChevronDown, faChevronRight, faEdit, faFile, faFolder, faFolderOpen, faTrash } from '@fortawesome/free-solid-svg-icons'; import { TreeviewItem } from 'app/exercises/programming/shared/code-editor/treeview/models/treeview-item'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { CodeEditorFileBrowserBadgeComponent } from './code-editor-file-browser-badge.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-code-editor-file-browser-folder', templateUrl: './code-editor-file-browser-folder.component.html', providers: [NgbModal], + imports: [FaIconComponent, CodeEditorFileBrowserBadgeComponent, ArtemisTranslatePipe], }) export class CodeEditorFileBrowserFolderComponent extends CodeEditorFileBrowserNodeComponent { @Input() onCollapseExpand: () => void; diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-node.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-node.component.ts index afe9806ffece..dbd4f7ac93b5 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-node.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser-node.component.ts @@ -2,7 +2,9 @@ import { Component, ElementRef, EventEmitter, Input, OnChanges, Output, SimpleCh import { FileType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { TreeviewItem } from 'app/exercises/programming/shared/code-editor/treeview/models/treeview-item'; -@Component({ template: '' }) +@Component({ + template: '', +}) export abstract class CodeEditorFileBrowserNodeComponent implements OnChanges { FileType = FileType; diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser.component.ts index 158129a2dab8..cfa724dad377 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges, ViewChild, inject } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { Observable, Subscription, of, throwError } from 'rxjs'; import { catchError, map as rxMap, switchMap, tap } from 'rxjs/operators'; @@ -27,6 +27,13 @@ import { TreeItem, TreeviewItem } from 'app/exercises/programming/shared/code-ed import { TreeviewComponent } from 'app/exercises/programming/shared/code-editor/treeview/components/treeview/treeview.component'; import { findItemInList } from 'app/exercises/programming/shared/code-editor/treeview/helpers/treeview-helper'; import { TEXT_FILE_EXTENSIONS } from 'app/shared/constants/file-extensions.constants'; +import { NgStyle } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CodeEditorFileBrowserCreateNodeComponent } from './code-editor-file-browser-create-node.component'; +import { CodeEditorFileBrowserFolderComponent } from './code-editor-file-browser-folder.component'; +import { CodeEditorFileBrowserFileComponent } from './code-editor-file-browser-file.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; export type InteractableEvent = { // Click event object; contains target information @@ -47,8 +54,25 @@ export interface FileTreeItem extends TreeItem { templateUrl: './code-editor-file-browser.component.html', styleUrls: ['./code-editor-file-browser.scss'], providers: [NgbModal], + imports: [ + NgStyle, + FaIconComponent, + TranslateDirective, + CodeEditorFileBrowserCreateNodeComponent, + TreeviewComponent, + CodeEditorStatusComponent, + CodeEditorFileBrowserFolderComponent, + CodeEditorFileBrowserFileComponent, + ArtemisTranslatePipe, + ], }) export class CodeEditorFileBrowserComponent implements OnInit, OnChanges, AfterViewInit, IFileDeleteDelegate { + modalService = inject(NgbModal); + private repositoryFileService = inject(CodeEditorRepositoryFileService); + private repositoryService = inject(CodeEditorRepositoryService); + private fileService = inject(CodeEditorFileService); + private conflictService = inject(CodeEditorConflictStateService); + CommitState = CommitState; FileType = FileType; @@ -142,14 +166,6 @@ export class CodeEditorFileBrowserComponent implements OnInit, OnChanges, AfterV this.commitStateChange.emit(commitState); } - constructor( - public modalService: NgbModal, - private repositoryFileService: CodeEditorRepositoryFileService, - private repositoryService: CodeEditorRepositoryService, - private fileService: CodeEditorFileService, - private conflictService: CodeEditorConflictStateService, - ) {} - ngOnInit(): void { this.conflictSubscription = this.conflictService.subscribeConflictState().subscribe((gitConflictState: GitConflictState) => { // When the git conflict was resolved, unset the selectedFile, as it can't be assured that it still exists. diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/header/code-editor-header.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/header/code-editor-header.component.ts index 63ec5588aa4d..b3e6abc479f2 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/header/code-editor-header.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/header/code-editor-header.component.ts @@ -1,14 +1,14 @@ import { ChangeDetectionStrategy, Component, input, model, output } from '@angular/core'; import { faFileAlt } from '@fortawesome/free-regular-svg-icons'; import { faCircleNotch, faGear } from '@fortawesome/free-solid-svg-icons'; +import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'; import { MAX_TAB_SIZE } from 'app/shared/monaco-editor/monaco-editor.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; @Component({ selector: 'jhi-code-editor-header', templateUrl: './code-editor-header.component.html', - imports: [ArtemisSharedModule], - standalone: true, + imports: [ArtemisSharedModule, NgbDropdown], changeDetection: ChangeDetectionStrategy.OnPush, }) export class CodeEditorHeaderComponent { diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/instructions/code-editor-instructions.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/instructions/code-editor-instructions.component.ts index 2739d8504f12..9a8c234b471e 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/instructions/code-editor-instructions.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/instructions/code-editor-instructions.component.ts @@ -3,11 +3,15 @@ import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons'; import { Interactable } from '@interactjs/core/Interactable'; import interact from 'interactjs'; +import { NgStyle } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-code-editor-instructions', styleUrls: ['./code-editor-instructions.scss'], templateUrl: './code-editor-instructions.component.html', + imports: [NgStyle, FaIconComponent, TranslateDirective], }) export class CodeEditorInstructionsComponent implements AfterViewInit { @Output() @@ -27,8 +31,6 @@ export class CodeEditorInstructionsComponent implements AfterViewInit { faChevronLeft = faChevronLeft; farListAlt = faListAlt; - constructor() {} - /** * After the view was initialized, we create an interact.js resizable object, * designate the edges which can be used to resize the target element and set min and max values. diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/layout/code-editor-grid.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/layout/code-editor-grid.component.ts index f770dd7de1ba..0ae80b132b95 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/layout/code-editor-grid.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/layout/code-editor-grid.component.ts @@ -1,18 +1,22 @@ -import { AfterViewInit, Component, ContentChild, ElementRef, EventEmitter, Input, Output, Renderer2, ViewChild, ViewEncapsulation } from '@angular/core'; +import { AfterViewInit, Component, ContentChild, ElementRef, EventEmitter, Input, Output, Renderer2, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { Interactable } from '@interactjs/core/Interactable'; import interact from 'interactjs'; import { ResizeType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { InteractableEvent } from 'app/exercises/programming/shared/code-editor/file-browser/code-editor-file-browser.component'; import { faGripLines, faGripLinesVertical } from '@fortawesome/free-solid-svg-icons'; import { CollapsableCodeEditorElement } from 'app/exercises/programming/shared/code-editor/container/code-editor-container.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-code-editor-grid', templateUrl: './code-editor-grid.component.html', styleUrls: ['./code-editor-grid.scss'], encapsulation: ViewEncapsulation.None, + imports: [FaIconComponent], }) export class CodeEditorGridComponent implements AfterViewInit { + private renderer = inject(Renderer2); + @ContentChild('editorSidebarRight', { static: false }) editorSidebarRight: ElementRef; @ContentChild('editorSidebarLeft', { static: false }) editorSidebarLeft: ElementRef; @ContentChild('editorBottomArea', { static: false }) editorBottomArea: ElementRef; @@ -48,8 +52,6 @@ export class CodeEditorGridComponent implements AfterViewInit { faGripLines = faGripLines; faGripLinesVertical = faGripLinesVertical; - constructor(private renderer: Renderer2) {} - /** * After the view was initialized, we create an interact.js resizable object, * designate the edges which can be used to resize the target element and set min and max values. diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/layout/code-editor-repository-is-locked.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/layout/code-editor-repository-is-locked.component.ts index 312443f90241..0c27d6a5e22d 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/layout/code-editor-repository-is-locked.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/layout/code-editor-repository-is-locked.component.ts @@ -1,5 +1,9 @@ import { Component } from '@angular/core'; import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-code-editor-repository-is-locked', @@ -16,6 +20,7 @@ import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'; `, styles: ['.locked-lable {font-size: 1.2rem; color: white}'], + imports: [FaIconComponent, TranslateDirective, NgbTooltip, ArtemisTranslatePipe], }) export class CodeEditorRepositoryIsLockedComponent { // Icons diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/monaco/code-editor-monaco.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/monaco/code-editor-monaco.component.ts index 60ea6ca7fa0e..008f3355249e 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/monaco/code-editor-monaco.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/monaco/code-editor-monaco.component.ts @@ -52,7 +52,6 @@ export type Annotation = { fileName: string; row: number; column: number; text: encapsulation: ViewEncapsulation.None, imports: [ArtemisSharedModule, ArtemisProgrammingManualAssessmentModule, MonacoEditorComponent, CodeEditorHeaderComponent], providers: [RepositoryFileService], - standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, }) export class CodeEditorMonacoComponent implements OnChanges { @@ -128,19 +127,13 @@ export class CodeEditorMonacoComponent implements OnChanges { annotationsArray: Array = []; constructor() { - effect( - () => { - this.feedbackInternal.set(this.feedbacks()); - }, - { allowSignalWrites: true }, - ); + effect(() => { + this.feedbackInternal.set(this.feedbacks()); + }); - effect( - () => { - this.feedbackSuggestionsInternal.set(this.feedbackSuggestions()); - }, - { allowSignalWrites: true }, - ); + effect(() => { + this.feedbackSuggestionsInternal.set(this.feedbackSuggestions()); + }); effect(() => { const annotations = this.buildAnnotations(); diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-conflict-state.service.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-conflict-state.service.ts index a045a3b2b62b..e3429e7e4901 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-conflict-state.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-conflict-state.service.ts @@ -1,8 +1,7 @@ import { BehaviorSubject, Observable } from 'rxjs'; -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { distinctUntilChanged } from 'rxjs/operators'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; -import { DomainService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain.service'; import { DomainType, GitConflictState } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { DomainDependentService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain-dependent.service'; @@ -17,14 +16,13 @@ export interface IConflictStateService { */ @Injectable({ providedIn: 'root' }) export class CodeEditorConflictStateService extends DomainDependentService implements IConflictStateService, OnDestroy { + private jhiWebsocketService = inject(JhiWebsocketService); + private conflictSubjects: Map> = new Map(); private websocketConnections: Map = new Map(); - constructor( - domainService: DomainService, - private jhiWebsocketService: JhiWebsocketService, - ) { - super(domainService); + constructor() { + super(); this.initDomainSubscription(); } diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain-dependent-endpoint.service.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain-dependent-endpoint.service.ts index b124042fec13..bd34fcd567a0 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain-dependent-endpoint.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain-dependent-endpoint.service.ts @@ -2,20 +2,18 @@ import { DomainDependentService } from 'app/exercises/programming/shared/code-ed import { HttpClient } from '@angular/common/http'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { DomainChange, DomainType } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; -import { DomainService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain.service'; +import { inject } from '@angular/core'; /** * Service that can be extended to update rest endpoint urls with the received domain information. */ export abstract class DomainDependentEndpointService extends DomainDependentService { protected restResourceUrl?: string; + protected http = inject(HttpClient); + protected jhiWebsocketService = inject(JhiWebsocketService); - protected constructor( - protected http: HttpClient, - protected jhiWebsocketService: JhiWebsocketService, - domainService: DomainService, - ) { - super(domainService); + protected constructor() { + super(); this.initDomainSubscription(); } diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain-dependent.service.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain-dependent.service.ts index 2445d8f9d351..9b259a208d9a 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain-dependent.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain-dependent.service.ts @@ -1,4 +1,4 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { DomainChange } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { Subscription } from 'rxjs'; import { filter, tap } from 'rxjs/operators'; @@ -9,11 +9,11 @@ import { DomainService } from 'app/exercises/programming/shared/code-editor/serv */ @Injectable({ providedIn: 'root' }) export abstract class DomainDependentService implements OnDestroy { + private domainService = inject(DomainService); + protected domain: DomainChange; protected domainChangeSubscription: Subscription; - protected constructor(private domainService: DomainService) {} - /** * Initializes a domain subscription. */ diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain.service.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain.service.ts index cebaae778af9..21213d9df226 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-domain.service.ts @@ -16,8 +16,6 @@ export class DomainService { protected domain: DomainChange; private subject = new BehaviorSubject(undefined); - constructor() {} - /** * Sets domain and subject.next according to parameter. * @param domain - defines new domain of the service. diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-repository.service.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-repository.service.ts index 172146cafe15..11fb43e73077 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-repository.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-repository.service.ts @@ -1,5 +1,5 @@ -import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http'; -import { Injectable, OnDestroy } from '@angular/core'; +import { HttpErrorResponse, HttpParams } from '@angular/common/http'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { Observable, Subject, UnaryFunction, of, pipe, throwError } from 'rxjs'; import { catchError, map, tap } from 'rxjs/operators'; import { @@ -14,8 +14,6 @@ import { } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; import { CodeEditorConflictStateService } from 'app/exercises/programming/shared/code-editor/service/code-editor-conflict-state.service'; import { BuildLogService } from 'app/exercises/programming/shared/service/build-log.service'; -import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; -import { DomainService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain.service'; import { DomainDependentEndpointService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain-dependent-endpoint.service'; import { downloadFile } from 'app/shared/util/download.util'; @@ -79,13 +77,11 @@ const handleErrorResponse = (conflictService: CodeEditorConflictStateService) @Injectable({ providedIn: 'root' }) export class CodeEditorRepositoryService extends DomainDependentEndpointService implements ICodeEditorRepositoryService { - constructor( - http: HttpClient, - jhiWebsocketService: JhiWebsocketService, - domainService: DomainService, - private conflictService: CodeEditorConflictStateService, - ) { - super(http, jhiWebsocketService, domainService); + private conflictService = inject(CodeEditorConflictStateService); + + // necessary to be used in providers array in component + constructor() { + super(); } getStatus = () => { @@ -118,13 +114,11 @@ export class CodeEditorRepositoryService extends DomainDependentEndpointService @Injectable({ providedIn: 'root' }) export class CodeEditorBuildLogService extends DomainDependentEndpointService { - constructor( - private buildLogService: BuildLogService, - http: HttpClient, - jhiWebsocketService: JhiWebsocketService, - domainService: DomainService, - ) { - super(http, jhiWebsocketService, domainService); + private buildLogService = inject(BuildLogService); + + // necessary to be used in providers array in component + constructor() { + super(); } getBuildLogs = () => { @@ -138,14 +132,13 @@ export class CodeEditorBuildLogService extends DomainDependentEndpointService { @Injectable({ providedIn: 'root' }) export class CodeEditorRepositoryFileService extends DomainDependentEndpointService implements ICodeEditorRepositoryFileService, OnDestroy { + private conflictService = inject(CodeEditorConflictStateService); + fileUpdateSubject = new Subject(); - constructor( - http: HttpClient, - jhiWebsocketService: JhiWebsocketService, - domainService: DomainService, - private conflictService: CodeEditorConflictStateService, - ) { - super(http, jhiWebsocketService, domainService); + + // necessary to be used in providers array in component + constructor() { + super(); } /** diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-submission.service.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-submission.service.ts index 31be0e3ab0e4..9b6dc3c6f120 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-submission.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/service/code-editor-submission.service.ts @@ -1,8 +1,7 @@ -import { Injectable, OnDestroy } from '@angular/core'; +import { Injectable, OnDestroy, inject } from '@angular/core'; import { AlertService } from 'app/core/util/alert.service'; import { Subject, Subscription } from 'rxjs'; import { map, tap } from 'rxjs/operators'; -import { DomainService } from 'app/exercises/programming/shared/code-editor/service/code-editor-domain.service'; import { ProgrammingSubmissionService, ProgrammingSubmissionState } from 'app/exercises/programming/participate/programming-submission.service'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { SolutionProgrammingExerciseParticipation } from 'app/entities/participation/solution-programming-exercise-participation.model'; @@ -14,17 +13,16 @@ import { DomainDependentService } from 'app/exercises/programming/shared/code-ed */ @Injectable({ providedIn: 'root' }) export class CodeEditorSubmissionService extends DomainDependentService implements OnDestroy { + private submissionService = inject(ProgrammingSubmissionService); + private alertService = inject(AlertService); + private participationId?: number; private exerciseId?: number; private isBuildingSubject = new Subject(); private submissionSubscription: Subscription; - constructor( - domainService: DomainService, - private submissionService: ProgrammingSubmissionService, - private alertService: AlertService, - ) { - super(domainService); + constructor() { + super(); this.initDomainSubscription(); } diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/status/code-editor-status.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/status/code-editor-status.component.ts index 4547d93259bc..da1342e39ac7 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/status/code-editor-status.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/status/code-editor-status.component.ts @@ -1,11 +1,16 @@ import { Component, Input } from '@angular/core'; import { faCheckCircle, faCircleNotch, faExclamationTriangle, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; import { CommitState, EditorState } from 'app/exercises/programming/shared/code-editor/model/code-editor.model'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-code-editor-status', templateUrl: './code-editor-status.component.html', providers: [], + imports: [NgbTooltip, FaIconComponent, TranslateDirective, ArtemisTranslatePipe], }) export class CodeEditorStatusComponent { CommitState = CommitState; diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/components/treeview-item/treeview-item.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/components/treeview-item/treeview-item.component.ts index bb8d03651e73..4a82fd6234a1 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/components/treeview-item/treeview-item.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/components/treeview-item/treeview-item.component.ts @@ -1,12 +1,13 @@ import { Component, EventEmitter, Input, Output, TemplateRef } from '@angular/core'; import { TreeviewItem } from '../../models/treeview-item'; import { TreeviewItemTemplateContext } from '../../models/treeview-item-template-context'; +import { NgTemplateOutlet } from '@angular/common'; @Component({ - // eslint-disable-next-line @angular-eslint/component-selector selector: 'treeview-item', templateUrl: './treeview-item.component.html', styleUrls: ['./treeview-item.component.scss'], + imports: [NgTemplateOutlet], }) export class TreeviewItemComponent { @Input() template: TemplateRef>; diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/components/treeview/treeview.component.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/components/treeview/treeview.component.ts index 6eaec7181425..13a8fba38ca6 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/components/treeview/treeview.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/components/treeview/treeview.component.ts @@ -1,12 +1,14 @@ import { Component, EventEmitter, Input, Output, TemplateRef } from '@angular/core'; import { TreeviewItem } from '../../models/treeview-item'; import { TreeviewItemTemplateContext } from '../../models/treeview-item-template-context'; +import { FormsModule } from '@angular/forms'; +import { TreeviewItemComponent } from '../treeview-item/treeview-item.component'; @Component({ - // eslint-disable-next-line @angular-eslint/component-selector selector: 'treeview', templateUrl: './treeview.component.html', styleUrls: ['./treeview.component.scss'], + imports: [FormsModule, TreeviewItemComponent], }) export class TreeviewComponent { @Input() itemTemplate: TemplateRef>; diff --git a/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/treeview.module.ts b/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/treeview.module.ts index 3c229ba788c4..0642b734b261 100644 --- a/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/treeview.module.ts +++ b/src/main/webapp/app/exercises/programming/shared/code-editor/treeview/treeview.module.ts @@ -5,8 +5,7 @@ import { TreeviewComponent } from './components/treeview/treeview.component'; import { TreeviewItemComponent } from './components/treeview-item/treeview-item.component'; @NgModule({ - imports: [FormsModule, CommonModule], - declarations: [TreeviewComponent, TreeviewItemComponent], + imports: [FormsModule, CommonModule, TreeviewComponent, TreeviewItemComponent], exports: [TreeviewComponent], }) export class TreeviewModule { diff --git a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-group.component.ts b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-group.component.ts index 64ed91bab723..9c69a23ff4f3 100644 --- a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-group.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-group.component.ts @@ -1,9 +1,12 @@ import { Component, Input } from '@angular/core'; import type { CommitInfo } from 'app/entities/programming/programming-submission.model'; +import { CommitsInfoRowComponent } from './commits-info-row/commits-info-row.component'; +import { NgStyle } from '@angular/common'; @Component({ selector: 'jhi-commits-info-group', templateUrl: './commits-info-group.component.html', + imports: [CommitsInfoRowComponent, NgStyle], }) export class CommitsInfoGroupComponent { @Input() commits: CommitInfo[]; diff --git a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-row/commits-info-row.component.ts b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-row/commits-info-row.component.ts index a944e452e1b4..dc2435949768 100644 --- a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-row/commits-info-row.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info-group/commits-info-row/commits-info-row.component.ts @@ -2,10 +2,18 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import type { CommitInfo } from 'app/entities/programming/programming-submission.model'; import { faCircle } from '@fortawesome/free-regular-svg-icons'; import { faAngleDown, faAngleLeft } from '@fortawesome/free-solid-svg-icons'; +import { RouterLink } from '@angular/router'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ResultComponent } from 'app/exercises/shared/result/result.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { TruncatePipe } from 'app/shared/pipes/truncate.pipe'; @Component({ selector: 'jhi-commits-info-row', templateUrl: './commits-info-row.component.html', + imports: [RouterLink, NgbTooltip, ResultComponent, FaIconComponent, TranslateDirective, ArtemisDatePipe, TruncatePipe], }) export class CommitsInfoRowComponent { @Input() commit: CommitInfo; diff --git a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info.component.ts b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info.component.ts index 28a46506caab..61d629913bd7 100644 --- a/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/commits-info/commits-info.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { CommitInfo, ProgrammingSubmission } from 'app/entities/programming/programming-submission.model'; import { ProgrammingExerciseParticipationService } from 'app/exercises/programming/manage/services/programming-exercise-participation.service'; import dayjs from 'dayjs/esm'; @@ -6,12 +6,19 @@ import { createCommitUrl } from 'app/exercises/programming/shared/utils/programm import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; import { PROFILE_LOCALVC } from 'app/app.constants'; import { Subscription } from 'rxjs'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { CommitsInfoGroupComponent } from './commits-info-group/commits-info-group.component'; +import { NgStyle } from '@angular/common'; @Component({ selector: 'jhi-commits-info', templateUrl: './commits-info.component.html', + imports: [TranslateDirective, CommitsInfoGroupComponent, NgStyle], }) export class CommitsInfoComponent implements OnInit, OnDestroy { + private programmingExerciseParticipationService = inject(ProgrammingExerciseParticipationService); + private profileService = inject(ProfileService); + @Input() commits?: CommitInfo[]; @Input() currentSubmissionHash?: string; @Input() previousSubmissionHash?: string; @@ -28,11 +35,6 @@ export class CommitsInfoComponent implements OnInit, OnDestroy { localVC = false; - constructor( - private programmingExerciseParticipationService: ProgrammingExerciseParticipationService, - private profileService: ProfileService, - ) {} - ngOnInit(): void { if (!this.commits) { if (this.participationId) { diff --git a/src/main/webapp/app/exercises/programming/shared/instructions-render/extensions/programming-exercise-plant-uml.extension.ts b/src/main/webapp/app/exercises/programming/shared/instructions-render/extensions/programming-exercise-plant-uml.extension.ts index 886520d929f4..7b691d4dccee 100644 --- a/src/main/webapp/app/exercises/programming/shared/instructions-render/extensions/programming-exercise-plant-uml.extension.ts +++ b/src/main/webapp/app/exercises/programming/shared/instructions-render/extensions/programming-exercise-plant-uml.extension.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ProgrammingExerciseTestCase } from 'app/entities/programming/programming-exercise-test-case.model'; import { ArtemisTextReplacementPlugin } from 'app/shared/markdown-editor/extensions/ArtemisTextReplacementPlugin'; import { escapeStringForUseInRegex } from 'app/shared/util/global.utils'; @@ -14,6 +14,9 @@ const testsColorRegex = /testsColor\((\s*[^()\s]+(\([^()]*\))?)\)/g; @Injectable({ providedIn: 'root' }) export class ProgrammingExercisePlantUmlExtensionWrapper extends ArtemisTextReplacementPlugin { + private programmingExerciseInstructionService = inject(ProgrammingExerciseInstructionService); + private plantUmlService = inject(ProgrammingExercisePlantUmlService); + private latestResult?: Result; private testCases?: ProgrammingExerciseTestCase[]; private injectableElementsFoundSubject = new Subject<() => void>(); @@ -21,10 +24,7 @@ export class ProgrammingExercisePlantUmlExtensionWrapper extends ArtemisTextRepl // unique index, even if multiple plant uml diagrams are shown from different problem statements on the same page (in different tabs) private plantUmlIndex = 0; - constructor( - private programmingExerciseInstructionService: ProgrammingExerciseInstructionService, - private plantUmlService: ProgrammingExercisePlantUmlService, - ) { + constructor() { super(); } diff --git a/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.html b/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.html index 6172aa304cf8..9c9fa0b791d4 100644 --- a/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.html +++ b/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.html @@ -1,6 +1,6 @@ @if (!isLoading) {
    - + @if (exercise && exercise.exerciseGroup) { diff --git a/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.ts b/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.ts index b978289acf7d..332213c6ae4b 100644 --- a/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instruction.component.ts @@ -41,32 +41,46 @@ import { ProgrammingExerciseInstructionService } from 'app/exercises/programming import { escapeStringForUseInRegex } from 'app/shared/util/global.utils'; import { ProgrammingExerciseInstructionTaskStatusComponent } from 'app/exercises/programming/shared/instructions-render/task/programming-exercise-instruction-task-status.component'; import { toObservable } from '@angular/core/rxjs-interop'; +import { ProgrammingExerciseInstructionStepWizardComponent } from './step-wizard/programming-exercise-instruction-step-wizard.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-programming-exercise-instructions', templateUrl: './programming-exercise-instruction.component.html', styleUrls: ['./programming-exercise-instruction.scss'], + imports: [ProgrammingExerciseInstructionStepWizardComponent, ExamExerciseUpdateHighlighterComponent, FaIconComponent], }) export class ProgrammingExerciseInstructionComponent implements OnChanges, OnDestroy { + private viewContainerRef = inject(ViewContainerRef); + private resultService = inject(ResultService); + private participationWebsocketService = inject(ParticipationWebsocketService); + private programmingExerciseTaskWrapper = inject(ProgrammingExerciseTaskExtensionWrapper); + private programmingExercisePlantUmlWrapper = inject(ProgrammingExercisePlantUmlExtensionWrapper); + private programmingExerciseParticipationService = inject(ProgrammingExerciseParticipationService); + private programmingExerciseGradingService = inject(ProgrammingExerciseGradingService); + private sanitizer = inject(DomSanitizer); + private programmingExerciseInstructionService = inject(ProgrammingExerciseInstructionService); + private appRef = inject(ApplicationRef); + private injector = inject(EnvironmentInjector); private themeService = inject(ThemeService); @Input() public exercise: ProgrammingExercise; @Input() public participation: Participation; @Input() generateHtmlEvents: Observable; @Input() personalParticipation: boolean; + // Emits an event if the instructions are not available via the problemStatement - @Output() - public onNoInstructionsAvailable = new EventEmitter(); + @Output() public onNoInstructionsAvailable = new EventEmitter(); @ViewChild(ExamExerciseUpdateHighlighterComponent) examExerciseUpdateHighlighterComponent: ExamExerciseUpdateHighlighterComponent; - public problemStatement: string; - public participationSubscription?: Subscription; + private problemStatement: string; + private participationSubscription?: Subscription; private testCasesSubscription?: Subscription; public isInitial = true; public isLoading: boolean; - public latestResultValue?: Result; + latestResultValue?: Result; // unique index, even if multiple tasks are shown from different problem statements on the same page (in different tabs) private taskIndex = 0; public tasks: TaskArray; @@ -77,7 +91,7 @@ export class ProgrammingExerciseInstructionComponent implements OnChanges, OnDes set latestResult(result: Result | undefined) { this.latestResultValue = result; - this.programmingExercisePlantUmlWrapper.setLatestResult(this.latestResult); + this.programmingExercisePlantUmlWrapper.setLatestResult(this.latestResultValue); this.programmingExercisePlantUmlWrapper.setTestCases(this.testCases); } @@ -86,7 +100,6 @@ export class ProgrammingExerciseInstructionComponent implements OnChanges, OnDes markdownExtensions: PluginSimple[]; private injectableContentFoundSubscription: Subscription; - private tasksSubscription: Subscription; private generateHtmlSubscription: Subscription; private testCases?: ProgrammingExerciseTestCase[]; private themeChangeSubscription = toObservable(this.themeService.currentTheme).subscribe(() => { @@ -98,19 +111,7 @@ export class ProgrammingExerciseInstructionComponent implements OnChanges, OnDes // Icons faSpinner = faSpinner; - constructor( - public viewContainerRef: ViewContainerRef, - private resultService: ResultService, - private participationWebsocketService: ParticipationWebsocketService, - private programmingExerciseTaskWrapper: ProgrammingExerciseTaskExtensionWrapper, - private programmingExercisePlantUmlWrapper: ProgrammingExercisePlantUmlExtensionWrapper, - private programmingExerciseParticipationService: ProgrammingExerciseParticipationService, - private programmingExerciseGradingService: ProgrammingExerciseGradingService, - private sanitizer: DomSanitizer, - private programmingExerciseInstructionService: ProgrammingExerciseInstructionService, - private appRef: ApplicationRef, - private injector: EnvironmentInjector, - ) { + constructor() { this.programmingExerciseTaskWrapper.viewContainerRef = this.viewContainerRef; } @@ -119,7 +120,7 @@ export class ProgrammingExerciseInstructionComponent implements OnChanges, OnDes * subscription for the participation's result needs to be set up. * @param changes */ - public ngOnChanges(changes: SimpleChanges) { + ngOnChanges(changes: SimpleChanges) { if (this.exercise?.isAtLeastTutor) { if (this.testCasesSubscription) { this.testCasesSubscription.unsubscribe(); @@ -232,10 +233,10 @@ export class ProgrammingExerciseInstructionComponent implements OnChanges, OnDes /** * Render the markdown into html. */ - updateMarkdown(): void { + updateMarkdown() { // make sure that always the correct result is set, before updating markdown // looks weird, but in setter of latestResult are setters of sub components invoked - this.latestResult = this.latestResult; + this.latestResult = this.latestResultValue; this.injectableContentForMarkdownCallbacks = []; this.renderMarkdown(); diff --git a/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instructions-render.module.ts b/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instructions-render.module.ts index fbfc110d7304..6ae307eb4875 100644 --- a/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instructions-render.module.ts +++ b/src/main/webapp/app/exercises/programming/shared/instructions-render/programming-exercise-instructions-render.module.ts @@ -9,8 +9,16 @@ import { ExamExerciseUpdateHighlighterModule } from 'app/exam/participate/exerci import { SafeHtmlPipe } from 'app/shared/pipes/safe-html.pipe'; @NgModule({ - imports: [ArtemisSharedModule, ArtemisResultModule, ArtemisMarkdownModule, ExamExerciseUpdateHighlighterModule, SafeHtmlPipe], - declarations: [ProgrammingExerciseInstructionComponent, ProgrammingExerciseInstructionStepWizardComponent, ProgrammingExerciseInstructionTaskStatusComponent], + imports: [ + ArtemisSharedModule, + ArtemisResultModule, + ArtemisMarkdownModule, + ExamExerciseUpdateHighlighterModule, + SafeHtmlPipe, + ProgrammingExerciseInstructionComponent, + ProgrammingExerciseInstructionStepWizardComponent, + ProgrammingExerciseInstructionTaskStatusComponent, + ], exports: [ProgrammingExerciseInstructionComponent], }) export class ArtemisProgrammingExerciseInstructionsRenderModule {} diff --git a/src/main/webapp/app/exercises/programming/shared/instructions-render/step-wizard/programming-exercise-instruction-step-wizard.component.ts b/src/main/webapp/app/exercises/programming/shared/instructions-render/step-wizard/programming-exercise-instruction-step-wizard.component.ts index 4bd4d7cea397..e5d5deda1062 100644 --- a/src/main/webapp/app/exercises/programming/shared/instructions-render/step-wizard/programming-exercise-instruction-step-wizard.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/instructions-render/step-wizard/programming-exercise-instruction-step-wizard.component.ts @@ -1,18 +1,24 @@ -import { Component, Input, OnChanges, SimpleChanges } from '@angular/core'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { Component, Input, OnChanges, SimpleChanges, inject } from '@angular/core'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { ProgrammingExerciseInstructionService, TestCaseState } from 'app/exercises/programming/shared/instructions-render/service/programming-exercise-instruction.service'; import { TaskArray } from 'app/exercises/programming/shared/instructions-render/task/programming-exercise-task.model'; import { FeedbackComponent } from 'app/exercises/shared/feedback/feedback.component'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { Result } from 'app/entities/result.model'; import { faCheck, faCircle, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-programming-exercise-instructions-step-wizard', templateUrl: './programming-exercise-instruction-step-wizard.component.html', styleUrls: ['./programming-exercise-instruction-step-wizard.scss'], + imports: [TranslateDirective, NgbTooltip, FaIconComponent], }) export class ProgrammingExerciseInstructionStepWizardComponent implements OnChanges { + private modalService = inject(NgbModal); + private instructionService = inject(ProgrammingExerciseInstructionService); + TestCaseState = TestCaseState; @Input() exercise: Exercise; @@ -26,11 +32,6 @@ export class ProgrammingExerciseInstructionStepWizardComponent implements OnChan faCheck = faCheck; faCircle = faCircle; - constructor( - private modalService: NgbModal, - private instructionService: ProgrammingExerciseInstructionService, - ) {} - /** * Life cycle hook called by Angular to indicate that changes are detected. * @param changes - change that is detected. diff --git a/src/main/webapp/app/exercises/programming/shared/instructions-render/task/programming-exercise-instruction-task-status.component.ts b/src/main/webapp/app/exercises/programming/shared/instructions-render/task/programming-exercise-instruction-task-status.component.ts index e85002c954af..68192ebc955c 100644 --- a/src/main/webapp/app/exercises/programming/shared/instructions-render/task/programming-exercise-instruction-task-status.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/instructions-render/task/programming-exercise-instruction-task-status.component.ts @@ -1,17 +1,24 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { faCheckCircle, faCircleDot, faTimesCircle } from '@fortawesome/free-regular-svg-icons'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { Exercise, ExerciseType } from 'app/entities/exercise.model'; import { Result } from 'app/entities/result.model'; import { ProgrammingExerciseInstructionService, TestCaseState } from 'app/exercises/programming/shared/instructions-render/service/programming-exercise-instruction.service'; import { FeedbackComponent } from 'app/exercises/shared/feedback/feedback.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; +import { SafeHtmlPipe } from 'app/shared/pipes/safe-html.pipe'; @Component({ selector: 'jhi-programming-exercise-instructions-task-status', templateUrl: './programming-exercise-instruction-task-status.component.html', styleUrls: ['./programming-exercise-instruction-task-status.scss'], + imports: [FaIconComponent, ArtemisTranslatePipe, SafeHtmlPipe], }) export class ProgrammingExerciseInstructionTaskStatusComponent { + private programmingExerciseInstructionService = inject(ProgrammingExerciseInstructionService); + private modalService = inject(NgbModal); + TestCaseState = TestCaseState; translationBasePath = 'artemisApp.editor.testStatusLabels.'; @@ -44,11 +51,6 @@ export class ProgrammingExerciseInstructionTaskStatusComponent { farCheckCircle = faCheckCircle; farTimesCircle = faTimesCircle; - constructor( - private programmingExerciseInstructionService: ProgrammingExerciseInstructionService, - private modalService: NgbModal, - ) {} - set testIds(testIds: number[]) { this.testIdsValue = testIds; const { diff --git a/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component.ts b/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component.ts index d1506497f4d6..366bb456e8d4 100644 --- a/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.component.ts @@ -1,4 +1,5 @@ -import { AfterViewInit, Component, Input, OnChanges, OnDestroy, OnInit, QueryList, SimpleChanges, ViewChildren, input } from '@angular/core'; +import { AfterViewInit, Component, Input, OnChanges, OnDestroy, OnInit, QueryList, SimpleChanges, ViewChildren, inject, input } from '@angular/core'; +import { ExerciseFeedbackSuggestionOptionsComponent } from 'app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.component'; import dayjs from 'dayjs/esm'; import { TranslateService } from '@ngx-translate/core'; import { AssessmentType } from 'app/entities/assessment-type.model'; @@ -14,13 +15,35 @@ import { ActivatedRoute } from '@angular/router'; import { tap } from 'rxjs/operators'; import { ImportOptions } from 'app/types/programming-exercises'; import { ProgrammingExerciseInputField } from 'app/exercises/programming/manage/update/programming-exercise-update.helper'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { AsyncPipe, NgStyle } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-programming-exercise-lifecycle', templateUrl: './programming-exercise-lifecycle.component.html', styleUrls: ['./programming-exercise-test-schedule-picker.scss'], + imports: [ + ProgrammingExerciseTestScheduleDatePickerComponent, + FormsModule, + TranslateDirective, + HelpIconComponent, + FaIconComponent, + NgStyle, + ExerciseFeedbackSuggestionOptionsComponent, + AsyncPipe, + ArtemisTranslatePipe, + ], }) export class ProgrammingExerciseLifecycleComponent implements AfterViewInit, OnDestroy, OnInit, OnChanges { + private translateService = inject(TranslateService); + private exerciseService = inject(ExerciseService); + private athenaService = inject(AthenaService); + private activatedRoute = inject(ActivatedRoute); + protected readonly assessmentType = AssessmentType; protected readonly IncludedInOverallScore = IncludedInOverallScore; protected readonly faCogs = faCogs; @@ -44,16 +67,9 @@ export class ProgrammingExerciseLifecycleComponent implements AfterViewInit, OnD isAthenaEnabled$: Observable | undefined; - isImport: boolean = false; + isImport = false; private urlSubscription: Subscription; - constructor( - private translateService: TranslateService, - private exerciseService: ExerciseService, - private athenaService: AthenaService, - private activatedRoute: ActivatedRoute, - ) {} - /** * If the programming exercise does not have an id, set the assessment Type to AUTOMATIC */ diff --git a/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.module.ts b/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.module.ts index 3a6a81c38b82..6e05465983bc 100644 --- a/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.module.ts +++ b/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-lifecycle.module.ts @@ -7,8 +7,14 @@ import { OwlDateTimeModule } from '@danielmoncada/angular-datetime-picker'; import { ExerciseFeedbackSuggestionOptionsModule } from 'app/exercises/shared/feedback-suggestion/exercise-feedback-suggestion-options.module'; @NgModule({ - imports: [ArtemisSharedComponentModule, OwlDateTimeModule, ArtemisSharedModule, ExerciseFeedbackSuggestionOptionsModule], - declarations: [ProgrammingExerciseLifecycleComponent, ProgrammingExerciseTestScheduleDatePickerComponent], + imports: [ + ArtemisSharedComponentModule, + OwlDateTimeModule, + ArtemisSharedModule, + ExerciseFeedbackSuggestionOptionsModule, + ProgrammingExerciseLifecycleComponent, + ProgrammingExerciseTestScheduleDatePickerComponent, + ], exports: [ProgrammingExerciseLifecycleComponent], }) export class ArtemisProgrammingExerciseLifecycleModule {} diff --git a/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-test-schedule-date-picker.component.ts b/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-test-schedule-date-picker.component.ts index e0766b849452..20e118b715d7 100644 --- a/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-test-schedule-date-picker.component.ts +++ b/src/main/webapp/app/exercises/programming/shared/lifecycle/programming-exercise-test-schedule-date-picker.component.ts @@ -1,8 +1,13 @@ import { Component, EventEmitter, Input, Output, ViewChild, forwardRef } from '@angular/core'; -import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgModel } from '@angular/forms'; +import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR, NgModel } from '@angular/forms'; import dayjs from 'dayjs/esm'; import { isDate } from 'app/shared/util/utils'; import { faCalendarCheck, faCalendarMinus, faCalendarPlus } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { OwlDateTimeModule } from '@danielmoncada/angular-datetime-picker'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-programming-exercise-test-schedule-date-picker', @@ -15,6 +20,7 @@ import { faCalendarCheck, faCalendarMinus, faCalendarPlus } from '@fortawesome/f }, ], styleUrls: ['./programming-exercise-test-schedule-picker.scss'], + imports: [TranslateDirective, HelpIconComponent, FormsModule, OwlDateTimeModule, FaIconComponent, ArtemisDatePipe], }) export class ProgrammingExerciseTestScheduleDatePickerComponent implements ControlValueAccessor { @ViewChild('dateInput', { static: false }) dateInput: NgModel; diff --git a/src/main/webapp/app/exercises/programming/shared/programming-exercise.module.ts b/src/main/webapp/app/exercises/programming/shared/programming-exercise.module.ts index 9953a9f5aa84..a9afe6cd545f 100644 --- a/src/main/webapp/app/exercises/programming/shared/programming-exercise.module.ts +++ b/src/main/webapp/app/exercises/programming/shared/programming-exercise.module.ts @@ -5,7 +5,7 @@ import { OrionModule } from 'app/shared/orion/orion.module'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { RouterModule } from '@angular/router'; import { ArtemisProgrammingExerciseGradingModule } from 'app/exercises/programming/manage/grading/programming-exercise-grading.module'; -import { FeatureToggleModule } from 'app/shared/feature-toggle/feature-toggle.module'; + import { ArtemisExerciseScoresModule } from 'app/exercises/shared/exercise-scores/exercise-scores.module'; import { OrionProgrammingExerciseComponent } from 'app/orion/management/orion-programming-exercise.component'; import { ArtemisProgrammingAssessmentModule } from 'app/exercises/programming/assess/programming-assessment.module'; @@ -21,7 +21,6 @@ import { CommitsInfoRowComponent } from 'app/exercises/programming/shared/commit @NgModule({ imports: [ ArtemisSharedModule, - FeatureToggleModule, RouterModule, OrionModule, ArtemisProgrammingExerciseStatusModule, @@ -30,8 +29,6 @@ import { CommitsInfoRowComponent } from 'app/exercises/programming/shared/commit ArtemisProgrammingAssessmentModule, ExerciseCategoriesModule, SubmissionResultStatusModule, - ], - declarations: [ ProgrammingExerciseComponent, ProgrammingExerciseCreateButtonsComponent, OrionProgrammingExerciseComponent, diff --git a/src/main/webapp/app/exercises/programming/shared/service/aeolus.service.ts b/src/main/webapp/app/exercises/programming/shared/service/aeolus.service.ts index 9c07e5caee1f..528be78989c1 100644 --- a/src/main/webapp/app/exercises/programming/shared/service/aeolus.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/service/aeolus.service.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { BuildAction, PlatformAction, ScriptAction } from 'app/entities/programming/build.action'; import { WindFile } from 'app/entities/programming/wind.file'; import { Observable } from 'rxjs'; @@ -8,9 +8,9 @@ import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/progr @Injectable({ providedIn: 'root' }) export class AeolusService { - private resourceUrl = 'api/aeolus'; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + private resourceUrl = 'api/aeolus'; /** * Fetches the aeolus template file for the given programming language diff --git a/src/main/webapp/app/exercises/programming/shared/service/build-log.service.ts b/src/main/webapp/app/exercises/programming/shared/service/build-log.service.ts index 3f749357f188..74926345a2c0 100644 --- a/src/main/webapp/app/exercises/programming/shared/service/build-log.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/service/build-log.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpClient, HttpParams } from '@angular/common/http'; import { BuildLogEntry } from 'app/entities/programming/build-log.model'; @@ -9,9 +9,9 @@ export interface IBuildLogService { @Injectable({ providedIn: 'root' }) export class BuildLogService implements IBuildLogService { - private assignmentResourceUrl = 'api/repository'; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + private assignmentResourceUrl = 'api/repository'; /** * Retrieves the build logs for a given participation and optionally, a given result. diff --git a/src/main/webapp/app/exercises/programming/shared/service/file-type.service.ts b/src/main/webapp/app/exercises/programming/shared/service/file-type.service.ts index cce6e02d4b2e..a656b814f4db 100644 --- a/src/main/webapp/app/exercises/programming/shared/service/file-type.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/service/file-type.service.ts @@ -9,8 +9,6 @@ const IGNORED_CHAR_CODES = [CHAR_CODE_TAB, CHAR_CODE_LINE_FEED, CHAR_CODE_CARRIA providedIn: 'root', }) export class FileTypeService { - constructor() {} - /** * Determines for a string whether it represents the content of a binary file. * This is done by checking for characters that would not typically be found in plain text files, e.g. the 0-byte. diff --git a/src/main/webapp/app/exercises/programming/shared/service/programming-language-feature/programming-language-feature.service.ts b/src/main/webapp/app/exercises/programming/shared/service/programming-language-feature/programming-language-feature.service.ts index e535502030ef..9188c2afaea0 100644 --- a/src/main/webapp/app/exercises/programming/shared/service/programming-language-feature/programming-language-feature.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/service/programming-language-feature/programming-language-feature.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ProgrammingLanguage, ProjectType } from 'app/entities/programming/programming-exercise.model'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; @@ -19,9 +19,11 @@ export type ProgrammingLanguageFeature = { @Injectable({ providedIn: 'root' }) export class ProgrammingLanguageFeatureService { + private profileService = inject(ProfileService); + private programmingLanguageFeatures: Map = new Map(); - constructor(private profileService: ProfileService) { + constructor() { this.profileService.getProfileInfo().subscribe((profileInfo) => { profileInfo.programmingLanguageFeatures.forEach((programmingLanguageFeature) => { this.programmingLanguageFeatures.set(programmingLanguageFeature.programmingLanguage, programmingLanguageFeature); diff --git a/src/main/webapp/app/exercises/programming/shared/service/theia.service.ts b/src/main/webapp/app/exercises/programming/shared/service/theia.service.ts index 3165730ddab7..c82734db10b9 100644 --- a/src/main/webapp/app/exercises/programming/shared/service/theia.service.ts +++ b/src/main/webapp/app/exercises/programming/shared/service/theia.service.ts @@ -1,14 +1,14 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Observable } from 'rxjs'; import { ProgrammingLanguage } from 'app/entities/programming/programming-exercise.model'; @Injectable({ providedIn: 'root' }) export class TheiaService { - private resourceUrl = 'api/theia'; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + private resourceUrl = 'api/theia'; /** * Fetches the theia images for the given programming language diff --git a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-create-form.component.ts b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-create-form.component.ts index 64cd23e5d90d..96f4e84bbd71 100644 --- a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-create-form.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-create-form.component.ts @@ -1,16 +1,24 @@ -import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core'; +import { AfterViewInit, Component, ElementRef, ViewChild, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { AlertService } from 'app/core/util/alert.service'; import { ApollonDiagramService } from 'app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.service'; import { ApollonDiagram } from 'app/entities/apollon-diagram.model'; import { faSave } from '@fortawesome/free-solid-svg-icons'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-apollon-diagram-create-form', templateUrl: './apollon-diagram-create-form.component.html', providers: [ApollonDiagramService], + imports: [FormsModule, TranslateDirective, FaIconComponent], }) export class ApollonDiagramCreateFormComponent implements AfterViewInit { + private activeModal = inject(NgbActiveModal); + private apollonDiagramService = inject(ApollonDiagramService); + private alertService = inject(AlertService); + apollonDiagram: ApollonDiagram; isSaving: boolean; @ViewChild('titleInput', { static: false }) titleInput: ElementRef; @@ -18,12 +26,6 @@ export class ApollonDiagramCreateFormComponent implements AfterViewInit { // Icons faSave = faSave; - constructor( - private activeModal: NgbActiveModal, - private apollonDiagramService: ApollonDiagramService, - private alertService: AlertService, - ) {} - /** * Adds focus on the title input field */ diff --git a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-detail.component.ts b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-detail.component.ts index ef93f826cc49..d245860e8084 100644 --- a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-detail.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-detail.component.ts @@ -1,7 +1,7 @@ -import { Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from '@angular/core'; +import { Component, ElementRef, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ApollonEditor, ApollonMode, Locale, UMLModel } from '@ls1intum/apollon'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbModalRef, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { JhiLanguageHelper } from 'app/core/language/language.helper'; import { convertRenderedSVGToPNG } from './exercise-generation/svg-renderer'; import { ApollonDiagramService } from 'app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.service'; @@ -16,14 +16,26 @@ import { CourseManagementService } from 'app/course/manage/course-management.ser import { DragAndDropQuestion } from 'app/entities/quiz/drag-and-drop-question.model'; import { ConfirmAutofocusModalComponent } from 'app/shared/components/confirm-autofocus-modal.component'; import { lastValueFrom } from 'rxjs'; -import { NgModel } from '@angular/forms'; +import { FormsModule, NgModel } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-apollon-diagram-detail', templateUrl: './apollon-diagram-detail.component.html', providers: [ApollonDiagramService], + imports: [TranslateDirective, FaIconComponent, FormsModule, NgbTooltip, ArtemisTranslatePipe], }) export class ApollonDiagramDetailComponent implements OnInit, OnDestroy { + private apollonDiagramService = inject(ApollonDiagramService); + private courseService = inject(CourseManagementService); + private alertService = inject(AlertService); + private translateService = inject(TranslateService); + private languageHelper = inject(JhiLanguageHelper); + private modalService = inject(NgbModal); + private route = inject(ActivatedRoute); + @ViewChild('editorContainer', { static: false }) editorContainer: ElementRef; @ViewChild('titleField') titleField?: NgModel; @@ -38,7 +50,7 @@ export class ApollonDiagramDetailComponent implements OnInit, OnDestroy { apollonDiagram?: ApollonDiagram; apollonEditor?: ApollonEditor; - isSaved: boolean = true; + isSaved = true; /** */ autoSaveInterval: number; @@ -71,16 +83,6 @@ export class ApollonDiagramDetailComponent implements OnInit, OnDestroy { faArrow = faArrowLeft; faX = faX; - constructor( - private apollonDiagramService: ApollonDiagramService, - private courseService: CourseManagementService, - private alertService: AlertService, - private translateService: TranslateService, - private languageHelper: JhiLanguageHelper, - private modalService: NgbModal, - private route: ActivatedRoute, - ) {} - /** * Initializes Apollon Editor and sets auto save timer */ diff --git a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-import-dialog.component.ts b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-import-dialog.component.ts index 6f62cf1de360..edc94bcea84d 100644 --- a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-import-dialog.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-import-dialog.component.ts @@ -1,22 +1,25 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ApollonDiagramService } from 'app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.service'; import { DragAndDropQuestion } from 'app/entities/quiz/drag-and-drop-question.model'; +import { ApollonDiagramListComponent } from './apollon-diagram-list.component'; +import { ApollonDiagramDetailComponent } from './apollon-diagram-detail.component'; @Component({ selector: 'jhi-apollon-diagram-import-dialog', templateUrl: './apollon-diagram-import-dialog.component.html', providers: [ApollonDiagramService], + imports: [ApollonDiagramListComponent, ApollonDiagramDetailComponent], }) export class ApollonDiagramImportDialogComponent { + private activeModal = inject(NgbActiveModal); + @Input() courseId: number; isInEditView = false; apollonDiagramDetailId: number; - constructor(private activeModal: NgbActiveModal) {} - handleDetailOpen(id: number) { this.isInEditView = true; this.apollonDiagramDetailId = id; diff --git a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-list.component.ts b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-list.component.ts index fbcb1c0fb0b2..97e729b61d97 100644 --- a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-list.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-list.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; import { ActivatedRoute } from '@angular/router'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; @@ -14,13 +14,27 @@ import { Course } from 'app/entities/course.model'; import { faPlus, faSort, faTimes, faX } from '@fortawesome/free-solid-svg-icons'; import { ButtonSize } from 'app/shared/components/button.component'; import { UMLDiagramType } from '@ls1intum/apollon'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; @Component({ selector: 'jhi-apollon-diagram-list', templateUrl: './apollon-diagram-list.component.html', providers: [ApollonDiagramService], + imports: [TranslateDirective, FaIconComponent, SortDirective, SortByDirective, DeleteButtonDirective], }) export class ApollonDiagramListComponent implements OnInit { + private apollonDiagramsService = inject(ApollonDiagramService); + private alertService = inject(AlertService); + private modalService = inject(NgbModal); + private sortService = inject(SortService); + private route = inject(ActivatedRoute); + private courseService = inject(CourseManagementService); + private accountService = inject(AccountService); + apollonDiagrams: ApollonDiagram[] = []; predicate: string; reverse: boolean; @@ -43,15 +57,7 @@ export class ApollonDiagramListComponent implements OnInit { ButtonSize = ButtonSize; - constructor( - private apollonDiagramsService: ApollonDiagramService, - private alertService: AlertService, - private modalService: NgbModal, - private sortService: SortService, - private route: ActivatedRoute, - private courseService: CourseManagementService, - private accountService: AccountService, - ) { + constructor() { this.predicate = 'id'; this.reverse = true; } diff --git a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.module.ts b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.module.ts index fb7448499f4c..b9104e1d2bc5 100644 --- a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.module.ts +++ b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.module.ts @@ -6,7 +6,6 @@ import { ApollonDiagramListComponent } from './apollon-diagram-list.component'; import { ArtemisSharedModule } from 'app/shared/shared.module'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [ApollonDiagramCreateFormComponent, ApollonDiagramDetailComponent, ApollonDiagramListComponent, ApollonDiagramImportDialogComponent], + imports: [ArtemisSharedModule, ApollonDiagramCreateFormComponent, ApollonDiagramDetailComponent, ApollonDiagramListComponent, ApollonDiagramImportDialogComponent], }) export class ArtemisApollonDiagramsModule {} diff --git a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.service.ts b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.service.ts index fd6fd5cb1fd7..f99733ab4681 100644 --- a/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.service.ts +++ b/src/main/webapp/app/exercises/quiz/manage/apollon-diagrams/apollon-diagram.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable, tap } from 'rxjs'; @@ -10,12 +10,10 @@ export type EntityResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class ApollonDiagramService { - private resourceUrl = 'api'; + private http = inject(HttpClient); + private entityTitleService = inject(EntityTitleService); - constructor( - private http: HttpClient, - private entityTitleService: EntityTitleService, - ) {} + private resourceUrl = 'api'; /** * Creates diagram. diff --git a/src/main/webapp/app/exercises/quiz/manage/drag-and-drop-question/drag-and-drop-question-edit.component.ts b/src/main/webapp/app/exercises/quiz/manage/drag-and-drop-question/drag-and-drop-question-edit.component.ts index 56ec3e7aaaec..ddb1427917b2 100644 --- a/src/main/webapp/app/exercises/quiz/manage/drag-and-drop-question/drag-and-drop-question-edit.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/drag-and-drop-question/drag-and-drop-question-edit.component.ts @@ -11,22 +11,37 @@ import { SimpleChanges, ViewChild, ViewEncapsulation, + inject, } from '@angular/core'; import { DragAndDropQuestionUtil } from 'app/exercises/quiz/shared/drag-and-drop-question-util.service'; import { DragAndDropMouseEvent } from 'app/exercises/quiz/manage/drag-and-drop-question/drag-and-drop-mouse-event.class'; import { DragState } from 'app/entities/quiz/drag-state.enum'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbCollapse, NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { DragAndDropMapping } from 'app/entities/quiz/drag-and-drop-mapping.model'; import { DragAndDropQuestion } from 'app/entities/quiz/drag-and-drop-question.model'; import { DragItem } from 'app/entities/quiz/drag-item.model'; import { DropLocation } from 'app/entities/quiz/drop-location.model'; import { QuizQuestionEdit } from 'app/exercises/quiz/manage/quiz-question-edit.interface'; +import { DragAndDropQuestionComponent } from 'app/exercises/quiz/shared/questions/drag-and-drop-question/drag-and-drop-question.component'; import { cloneDeep } from 'lodash-es'; import { round } from 'app/shared/util/utils'; import { MAX_SIZE_UNIT } from 'app/exercises/quiz/manage/apollon-diagrams/exercise-generation/quiz-exercise-generator'; import { debounceTime, filter } from 'rxjs/operators'; import { ImageLoadingStatus, SecuredImageComponent } from 'app/shared/image/secured-image.component'; import { generateExerciseHintExplanation } from 'app/shared/util/markdown.util'; +import { faFileImage } from '@fortawesome/free-regular-svg-icons'; +import { CdkDrag, CdkDragDrop, CdkDragPlaceholder, CdkDragPreview, CdkDropList, CdkDropListGroup } from '@angular/cdk/drag-drop'; +import { MAX_QUIZ_QUESTION_POINTS } from 'app/shared/constants/input.constants'; +import { FileService } from 'app/shared/http/file.service'; +import { QuizHintAction } from 'app/shared/monaco-editor/model/actions/quiz/quiz-hint.action'; +import { QuizExplanationAction } from 'app/shared/monaco-editor/model/actions/quiz/quiz-explanation.action'; +import { MarkdownEditorMonacoComponent, TextWithDomainAction } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { QuizScoringInfoModalComponent } from '../quiz-scoring-info-modal/quiz-scoring-info-modal.component'; +import { NgClass, NgStyle, NgTemplateOutlet } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { faAngleDown, faAngleRight, @@ -45,13 +60,6 @@ import { faUnlink, faUpload, } from '@fortawesome/free-solid-svg-icons'; -import { faFileImage } from '@fortawesome/free-regular-svg-icons'; -import { CdkDragDrop } from '@angular/cdk/drag-drop'; -import { MAX_QUIZ_QUESTION_POINTS } from 'app/shared/constants/input.constants'; -import { FileService } from 'app/shared/http/file.service'; -import { QuizHintAction } from 'app/shared/monaco-editor/model/actions/quiz/quiz-hint.action'; -import { QuizExplanationAction } from 'app/shared/monaco-editor/model/actions/quiz/quiz-explanation.action'; -import { MarkdownEditorMonacoComponent, TextWithDomainAction } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; @Component({ selector: 'jhi-drag-and-drop-question-edit', @@ -59,8 +67,33 @@ import { MarkdownEditorMonacoComponent, TextWithDomainAction } from 'app/shared/ providers: [DragAndDropQuestionUtil], styleUrls: ['./drag-and-drop-question-edit.component.scss', '../quiz-exercise.scss', '../../shared/quiz.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + FaIconComponent, + FormsModule, + TranslateDirective, + NgbTooltip, + NgbCollapse, + QuizScoringInfoModalComponent, + MarkdownEditorMonacoComponent, + CdkDropListGroup, + SecuredImageComponent, + NgClass, + CdkDropList, + NgStyle, + CdkDrag, + CdkDragPreview, + NgTemplateOutlet, + CdkDragPlaceholder, + DragAndDropQuestionComponent, + ArtemisTranslatePipe, + ], }) export class DragAndDropQuestionEditComponent implements OnInit, OnChanges, AfterViewInit, QuizQuestionEdit { + private dragAndDropQuestionUtil = inject(DragAndDropQuestionUtil); + private modalService = inject(NgbModal); + private changeDetector = inject(ChangeDetectorRef); + private fileService = inject(FileService); + @ViewChild('clickLayer', { static: false }) private clickLayer: ElementRef; @ViewChild('backgroundImage ', { static: false }) private backgroundImage: SecuredImageComponent; @ViewChild('markdownEditor', { static: false }) private markdownEditor: MarkdownEditorMonacoComponent; @@ -131,13 +164,6 @@ export class DragAndDropQuestionEditComponent implements OnInit, OnChanges, Afte readonly MAX_POINTS = MAX_QUIZ_QUESTION_POINTS; - constructor( - private dragAndDropQuestionUtil: DragAndDropQuestionUtil, - private modalService: NgbModal, - private changeDetector: ChangeDetectorRef, - private fileService: FileService, - ) {} - /** * Actions when initializing component. */ diff --git a/src/main/webapp/app/exercises/quiz/manage/match-percentage-info-modal/match-percentage-info-modal.component.ts b/src/main/webapp/app/exercises/quiz/manage/match-percentage-info-modal/match-percentage-info-modal.component.ts index 447ea315e40b..07f4c1477bfd 100644 --- a/src/main/webapp/app/exercises/quiz/manage/match-percentage-info-modal/match-percentage-info-modal.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/match-percentage-info-modal/match-percentage-info-modal.component.ts @@ -1,15 +1,19 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { faQuestionCircle } from '@fortawesome/free-regular-svg-icons'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-match-percentage-info-modal', templateUrl: './match-percentage-info-modal.component.html', + imports: [TranslateDirective, FaIconComponent], }) export class MatchPercentageInfoModalComponent { + private modalService = inject(NgbModal); + // Icons farQuestionCircle = faQuestionCircle; - constructor(private modalService: NgbModal) {} /** * Open a large modal with the given content. diff --git a/src/main/webapp/app/exercises/quiz/manage/multiple-choice-question/multiple-choice-question-edit.component.ts b/src/main/webapp/app/exercises/quiz/manage/multiple-choice-question/multiple-choice-question-edit.component.ts index 553aa5f3edf8..5d917c4a0c49 100644 --- a/src/main/webapp/app/exercises/quiz/manage/multiple-choice-question/multiple-choice-question-edit.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/multiple-choice-question/multiple-choice-question-edit.component.ts @@ -1,8 +1,9 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; +import { NgbCollapse, NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { AnswerOption } from 'app/entities/quiz/answer-option.model'; import { MultipleChoiceQuestion } from 'app/entities/quiz/multiple-choice-question.model'; import { QuizQuestionEdit } from 'app/exercises/quiz/manage/quiz-question-edit.interface'; +import { MultipleChoiceQuestionComponent } from 'app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-question.component'; import { generateExerciseHintExplanation } from 'app/shared/util/markdown.util'; import { faAngleDown, faAngleRight, faQuestionCircle, faTrash } from '@fortawesome/free-solid-svg-icons'; import { ScoringType } from 'app/entities/quiz/quiz-question.model'; @@ -13,6 +14,11 @@ import { CorrectMultipleChoiceAnswerAction } from 'app/shared/monaco-editor/mode import { QuizExplanationAction } from 'app/shared/monaco-editor/model/actions/quiz/quiz-explanation.action'; import { MarkdownEditorMonacoComponent, TextWithDomainAction } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; import { MultipleChoiceVisualQuestionComponent } from 'app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-visual-question.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { QuizScoringInfoModalComponent } from '../quiz-scoring-info-modal/quiz-scoring-info-modal.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-multiple-choice-question-edit', @@ -20,8 +26,23 @@ import { MultipleChoiceVisualQuestionComponent } from 'app/exercises/quiz/shared styleUrls: ['../quiz-exercise.scss', '../../shared/quiz.scss'], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + FaIconComponent, + FormsModule, + TranslateDirective, + NgbCollapse, + QuizScoringInfoModalComponent, + NgbTooltip, + MarkdownEditorMonacoComponent, + MultipleChoiceQuestionComponent, + MultipleChoiceVisualQuestionComponent, + ArtemisTranslatePipe, + ], }) export class MultipleChoiceQuestionEditComponent implements OnInit, QuizQuestionEdit { + private modalService = inject(NgbModal); + private changeDetector = inject(ChangeDetectorRef); + @ViewChild('markdownEditor', { static: false }) private markdownEditor: MarkdownEditorMonacoComponent; @@ -63,11 +84,6 @@ export class MultipleChoiceQuestionEditComponent implements OnInit, QuizQuestion readonly MAX_POINTS = MAX_QUIZ_QUESTION_POINTS; - constructor( - private modalService: NgbModal, - private changeDetector: ChangeDetectorRef, - ) {} - /** * Init the question editor text by parsing the markdown. */ diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-confirm-import-invalid-questions-modal.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-confirm-import-invalid-questions-modal.component.ts index 9c690f072e19..a4bb1ee35156 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-confirm-import-invalid-questions-modal.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-confirm-import-invalid-questions-modal.component.ts @@ -1,21 +1,26 @@ -import { Component, EventEmitter } from '@angular/core'; +import { Component, EventEmitter, inject } from '@angular/core'; import { faBan, faExclamationTriangle, faTimes } from '@fortawesome/free-solid-svg-icons'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { ValidationReason } from 'app/entities/exercise.model'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { JsonPipe } from '@angular/common'; @Component({ selector: 'jhi-quiz-confirm-import-invalid-questions-modal', templateUrl: './quiz-confirm-import-invalid-questions-modal.component.html', styleUrls: ['./quiz-confirm-import-invalid-questions-modal.scss'], + imports: [FormsModule, TranslateDirective, FaIconComponent, JsonPipe], }) export class QuizConfirmImportInvalidQuestionsModalComponent { + activeModal = inject(NgbActiveModal); + // Icons faBan = faBan; faTimes = faTimes; faExclamationTriangle = faExclamationTriangle; - constructor(public activeModal: NgbActiveModal) {} - invalidFlaggedQuestions: ValidationReason[]; shouldImport: EventEmitter = new EventEmitter(); diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-create-buttons.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-create-buttons.component.ts index 80924d7e8254..fb7b697885cd 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-create-buttons.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-create-buttons.component.ts @@ -1,17 +1,23 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, inject } from '@angular/core'; import { Course } from 'app/entities/course.model'; import { faCheckDouble, faFileExport, faFileImport, faPlus } from '@fortawesome/free-solid-svg-icons'; import { ExerciseImportWrapperComponent } from 'app/exercises/shared/import/exercise-import-wrapper/exercise-import-wrapper.component'; import { ExerciseType } from 'app/entities/exercise.model'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { Router } from '@angular/router'; +import { Router, RouterLink } from '@angular/router'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-quiz-exercise-create-buttons', templateUrl: './quiz-exercise-create-buttons.component.html', + imports: [RouterLink, FaIconComponent, TranslateDirective], }) export class QuizExerciseCreateButtonsComponent { + private router = inject(Router); + private modalService = inject(NgbModal); + @Input() course: Course; @Input() quizExercisesCount: number; @@ -21,11 +27,6 @@ export class QuizExerciseCreateButtonsComponent { faFileExport = faFileExport; faCheckDouble = faCheckDouble; - constructor( - private router: Router, - private modalService: NgbModal, - ) {} - /** * Opens the import modal for a quiz exercise */ diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-detail.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-detail.component.ts index 77166e7bfb2b..e12d777b0826 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-detail.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-detail.component.ts @@ -1,5 +1,6 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; +import { ExerciseDetailStatisticsComponent } from 'app/exercises/shared/statistics/exercise-detail-statistics.component'; import dayjs from 'dayjs/esm'; import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.service'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; @@ -13,12 +14,30 @@ import { ExerciseManagementStatisticsDto } from 'app/exercises/shared/statistics import { StatisticsService } from 'app/shared/statistics-graph/statistics.service'; import { TranslateService } from '@ngx-translate/core'; import { QuizQuestionType } from 'app/entities/quiz/quiz-question.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { QuizExerciseManageButtonsComponent } from './quiz-exercise-manage-buttons.component'; +import { QuizExerciseLifecycleButtonsComponent } from './quiz-exercise-lifecycle-buttons.component'; +import { DetailOverviewListComponent } from 'app/detail-overview-list/detail-overview-list.component'; @Component({ selector: 'jhi-quiz-exercise-detail', templateUrl: './quiz-exercise-detail.component.html', + imports: [ + TranslateDirective, + DocumentationButtonComponent, + QuizExerciseManageButtonsComponent, + QuizExerciseLifecycleButtonsComponent, + ExerciseDetailStatisticsComponent, + DetailOverviewListComponent, + ], }) export class QuizExerciseDetailComponent implements OnInit { + private route = inject(ActivatedRoute); + private quizExerciseService = inject(QuizExerciseService); + private statisticsService = inject(StatisticsService); + private translateService = inject(TranslateService); + readonly documentationType: DocumentationType = 'Quiz'; readonly dayjs = dayjs; @@ -33,13 +52,6 @@ export class QuizExerciseDetailComponent implements OnInit { detailOverviewSections: DetailOverviewSection[]; - constructor( - private route: ActivatedRoute, - private quizExerciseService: QuizExerciseService, - private statisticsService: StatisticsService, - private translateService: TranslateService, - ) {} - /** * Load the quizzes of the course for export on init. */ diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-export.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-export.component.ts index 149419c039ca..816f33e08c60 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-export.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-export.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { TranslateService } from '@ngx-translate/core'; @@ -10,26 +10,27 @@ import { CourseManagementService } from 'app/course/manage/course-management.ser import { QuizQuestion } from 'app/entities/quiz/quiz-question.model'; import { Course } from 'app/entities/course.model'; import { onError } from 'app/shared/util/global.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; @Component({ selector: 'jhi-quiz-exercise-export', templateUrl: './quiz-exercise-export.component.html', styleUrls: ['./quiz-exercise-export.component.scss', '../shared/quiz.scss'], + imports: [TranslateDirective, FormsModule], }) export class QuizExerciseExportComponent implements OnInit { + private route = inject(ActivatedRoute); + private quizExerciseService = inject(QuizExerciseService); + private courseService = inject(CourseManagementService); + private alertService = inject(AlertService); + private router = inject(Router); + private translateService = inject(TranslateService); + questions: QuizQuestion[] = new Array(0); courseId: number; course: Course; - constructor( - private route: ActivatedRoute, - private quizExerciseService: QuizExerciseService, - private courseService: CourseManagementService, - private alertService: AlertService, - private router: Router, - private translateService: TranslateService, - ) {} - /** * Load the quizzes of the course for export on init. */ diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-lifecycle-buttons.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-lifecycle-buttons.component.ts index 8732f2f297ff..bbd9a2b84872 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-lifecycle-buttons.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-lifecycle-buttons.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output, inject } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { QuizExercise, QuizMode, QuizStatus } from 'app/entities/quiz/quiz-exercise.model'; import { QuizExerciseService } from './quiz-exercise.service'; @@ -6,12 +6,19 @@ import { ActionType } from 'app/shared/delete-dialog/delete-dialog.model'; import { AlertService } from 'app/core/util/alert.service'; import { faEye, faFileExport, faPlayCircle, faPlus, faSignal, faSort, faStopCircle, faTable, faTimes, faWrench } from '@fortawesome/free-solid-svg-icons'; import { Subject } from 'rxjs'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; @Component({ selector: 'jhi-quiz-exercise-lifecycle-buttons', templateUrl: './quiz-exercise-lifecycle-buttons.component.html', + imports: [FaIconComponent, TranslateDirective, DeleteButtonDirective], }) export class QuizExerciseLifecycleButtonsComponent { + private quizExerciseService = inject(QuizExerciseService); + private alertService = inject(AlertService); + protected readonly QuizMode = QuizMode; protected readonly QuizStatus = QuizStatus; protected readonly ActionType = ActionType; @@ -40,11 +47,6 @@ export class QuizExerciseLifecycleButtonsComponent { @Output() handleNewQuizExercise = new EventEmitter(); - constructor( - private quizExerciseService: QuizExerciseService, - private alertService: AlertService, - ) {} - /** * Set the quiz open for practice */ diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-manage-buttons.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-manage-buttons.component.ts index 3575e81897b1..b7184d99cbe9 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-manage-buttons.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-manage-buttons.component.ts @@ -1,8 +1,8 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { QuizExerciseService } from './quiz-exercise.service'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { ActionType } from 'app/shared/delete-dialog/delete-dialog.model'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; import { AlertService } from 'app/core/util/alert.service'; @@ -10,12 +10,24 @@ import { EventManager } from 'app/core/util/event-manager.service'; import { faClipboardCheck, faEye, faFileExport, faListAlt, faSignal, faTable, faTrash, faUndo, faWrench } from '@fortawesome/free-solid-svg-icons'; import { Subject } from 'rxjs'; import { ButtonSize, ButtonType } from 'app/shared/components/button.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; @Component({ selector: 'jhi-quiz-exercise-manage-buttons', templateUrl: './quiz-exercise-manage-buttons.component.html', + imports: [RouterLink, FaIconComponent, TranslateDirective, ButtonComponent, DeleteButtonDirective], }) export class QuizExerciseManageButtonsComponent implements OnInit { + private quizExerciseService = inject(QuizExerciseService); + private eventManager = inject(EventManager); + private alertService = inject(AlertService); + private exerciseService = inject(ExerciseService); + private route = inject(ActivatedRoute); + private router = inject(Router); + protected readonly ActionType = ActionType; readonly faEye = faEye; readonly faSignal = faSignal; @@ -40,15 +52,6 @@ export class QuizExerciseManageButtonsComponent implements OnInit { baseUrl: string; isEvaluatingQuizExercise: boolean; - constructor( - private quizExerciseService: QuizExerciseService, - private eventManager: EventManager, - private alertService: AlertService, - private exerciseService: ExerciseService, - private route: ActivatedRoute, - private router: Router, - ) {} - @Input() isDetailPage: boolean; diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-paging.service.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-paging.service.ts index 305f36c48fbb..1dfaa760e998 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-paging.service.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-paging.service.ts @@ -1,12 +1,15 @@ import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { ExercisePagingService } from 'app/exercises/shared/manage/exercise-paging.service'; @Injectable({ providedIn: 'root' }) export class QuizExercisePagingService extends ExercisePagingService { private static readonly RESOURCE_URL = 'api/quiz-exercises'; - constructor(http: HttpClient) { + + constructor() { + const http = inject(HttpClient); + super(http, QuizExercisePagingService.RESOURCE_URL); } } diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-popup.service.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-popup.service.ts index dcbfdc19ae4f..a16d2d65df21 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-popup.service.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-popup.service.ts @@ -1,4 +1,4 @@ -import { Component, Injectable } from '@angular/core'; +import { Component, Injectable, inject } from '@angular/core'; import { Router } from '@angular/router'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { QuizExerciseService } from './quiz-exercise.service'; @@ -6,13 +6,13 @@ import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; @Injectable({ providedIn: 'root' }) export class QuizExercisePopupService { + private modalService = inject(NgbModal); + private router = inject(Router); + private quizExerciseService = inject(QuizExerciseService); + private ngbModalRef: NgbModalRef | null; - constructor( - private modalService: NgbModal, - private router: Router, - private quizExerciseService: QuizExerciseService, - ) { + constructor() { this.ngbModalRef = null; } diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.ts index 0c03bd54d888..1e9e3761cb47 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-update.component.ts @@ -1,4 +1,7 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnChanges, OnInit, SimpleChanges, ViewChild, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnChanges, OnInit, SimpleChanges, ViewChild, ViewEncapsulation, inject } from '@angular/core'; +import { DifficultyPickerComponent } from 'app/exercises/shared/difficulty-picker/difficulty-picker.component'; +import { ExerciseTitleChannelNameComponent } from 'app/exercises/shared/exercise-title-channel-name/exercise-title-channel-name.component'; +import { IncludedInOverallScorePickerComponent } from 'app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.component'; import { QuizExerciseService } from './quiz-exercise.service'; import { ActivatedRoute, Router } from '@angular/router'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; @@ -8,7 +11,7 @@ import { DragAndDropQuestionUtil } from 'app/exercises/quiz/shared/drag-and-drop import { ShortAnswerQuestionUtil } from 'app/exercises/quiz/shared/short-answer-question-util.service'; import { TranslateService } from '@ngx-translate/core'; import { Duration } from './quiz-exercise-interfaces'; -import { NgbDate, NgbModal, NgbModalOptions, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { NgbDate, NgbModal, NgbModalOptions, NgbModalRef, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import dayjs from 'dayjs/esm'; import { AlertService } from 'app/core/util/alert.service'; import { ComponentCanDeactivate } from 'app/shared/guard/can-deactivate.model'; @@ -33,6 +36,17 @@ import { QuizQuestionListEditComponent } from 'app/exercises/quiz/manage/quiz-qu import { DragAndDropQuestion } from 'app/entities/quiz/drag-and-drop-question.model'; import { GenericConfirmationDialogComponent } from 'app/overview/course-conversations/dialogs/generic-confirmation-dialog/generic-confirmation-dialog.component'; import { ShortAnswerQuestion } from 'app/entities/quiz/short-answer-question.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component'; +import { FormsModule } from '@angular/forms'; +import { HelpIconComponent } from 'app/shared/components/help-icon.component'; +import { CategorySelectorComponent } from 'app/shared/category-selector/category-selector.component'; +import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { CompetencySelectionComponent } from 'app/shared/competency-selection/competency-selection.component'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { JsonPipe, NgClass } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-quiz-exercise-detail', @@ -41,8 +55,39 @@ import { ShortAnswerQuestion } from 'app/entities/quiz/short-answer-question.mod providers: [DragAndDropQuestionUtil, ShortAnswerQuestionUtil], styleUrls: ['./quiz-exercise-update.component.scss', '../shared/quiz.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + TranslateDirective, + DocumentationButtonComponent, + FormsModule, + ExerciseTitleChannelNameComponent, + HelpIconComponent, + CategorySelectorComponent, + DifficultyPickerComponent, + FormDateTimePickerComponent, + ButtonComponent, + IncludedInOverallScorePickerComponent, + CompetencySelectionComponent, + QuizQuestionListEditComponent, + NgbTooltip, + FaIconComponent, + NgClass, + JsonPipe, + ArtemisTranslatePipe, + ], }) export class QuizExerciseUpdateComponent extends QuizExerciseValidationDirective implements OnInit, OnChanges, ComponentCanDeactivate { + private route = inject(ActivatedRoute); + private courseService = inject(CourseManagementService); + private quizExerciseService = inject(QuizExerciseService); + private router = inject(Router); + private translateService = inject(TranslateService); + private exerciseService = inject(ExerciseService); + private alertService = inject(AlertService); + private changeDetector = inject(ChangeDetectorRef); + private exerciseGroupService = inject(ExerciseGroupService); + private navigationUtilService = inject(ArtemisNavigationUtilService); + private modalService = inject(NgbModal); + @ViewChild('quizQuestionsEdit') quizQuestionListEditComponent: QuizQuestionListEditComponent; @@ -101,24 +146,6 @@ export class QuizExerciseUpdateComponent extends QuizExerciseValidationDirective centered: true, }; - constructor( - private route: ActivatedRoute, - private courseService: CourseManagementService, - private quizExerciseService: QuizExerciseService, - private router: Router, - private translateService: TranslateService, - private exerciseService: ExerciseService, - private alertService: AlertService, - public changeDetector: ChangeDetectorRef, - private exerciseGroupService: ExerciseGroupService, - private navigationUtilService: ArtemisNavigationUtilService, - dragAndDropQuestionUtil: DragAndDropQuestionUtil, - shortAnswerQuestionUtil: ShortAnswerQuestionUtil, - private modalService: NgbModal, - ) { - super(dragAndDropQuestionUtil, shortAnswerQuestionUtil); - } - /** * Initialize variables and load course and quiz from server. */ diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-validation.directive.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-validation.directive.ts index c32fa0d96453..a476655f060c 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-validation.directive.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise-validation.directive.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Directive } from '@angular/core'; +import { ChangeDetectorRef, Directive, inject } from '@angular/core'; import { QuizExercise, QuizMode } from 'app/entities/quiz/quiz-exercise.model'; import { QuizQuestion, QuizQuestionType } from 'app/entities/quiz/quiz-question.model'; import { MultipleChoiceQuestion } from 'app/entities/quiz/multiple-choice-question.model'; @@ -16,6 +16,9 @@ import { ShortAnswerQuestionUtil } from 'app/exercises/quiz/shared/short-answer- @Directive() export abstract class QuizExerciseValidationDirective { + protected dragAndDropQuestionUtil = inject(DragAndDropQuestionUtil); + protected shortAnswerQuestionUtil = inject(ShortAnswerQuestionUtil); + // Make constants available to html for comparison readonly DRAG_AND_DROP = QuizQuestionType.DRAG_AND_DROP; readonly MULTIPLE_CHOICE = QuizQuestionType.MULTIPLE_CHOICE; @@ -37,11 +40,6 @@ export abstract class QuizExerciseValidationDirective { protected invalidFlaggedQuestions: InvalidFlaggedQuestions = {}; pendingChangesCache: boolean; - protected constructor( - protected dragAndDropQuestionUtil: DragAndDropQuestionUtil, - protected shortAnswerQuestionUtil: ShortAnswerQuestionUtil, - ) {} - /** * 1. Check whether the inputs in the quiz are valid * 2. Check if warning are needed for the inputs diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise.component.ts index 22ac2e1a7685..3b219c2f8626 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise.component.ts @@ -10,10 +10,34 @@ import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service' import { AlertService } from 'app/core/util/alert.service'; import { faSort, faTrash } from '@fortawesome/free-solid-svg-icons'; import { isQuizEditable } from 'app/exercises/quiz/shared/quiz-manage-util.service'; +import { SortDirective } from 'app/shared/sort/sort.directive'; +import { FormsModule } from '@angular/forms'; +import { SortByDirective } from 'app/shared/sort/sort-by.directive'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { RouterLink } from '@angular/router'; +import { QuizExerciseLifecycleButtonsComponent } from './quiz-exercise-lifecycle-buttons.component'; +import { ExerciseCategoriesComponent } from 'app/shared/exercise-categories/exercise-categories.component'; +import { QuizExerciseManageButtonsComponent } from './quiz-exercise-manage-buttons.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; @Component({ selector: 'jhi-quiz-exercise', templateUrl: './quiz-exercise.component.html', + imports: [ + SortDirective, + FormsModule, + SortByDirective, + TranslateDirective, + FaIconComponent, + RouterLink, + QuizExerciseLifecycleButtonsComponent, + ExerciseCategoriesComponent, + QuizExerciseManageButtonsComponent, + DeleteButtonDirective, + ArtemisDatePipe, + ], }) export class QuizExerciseComponent extends ExerciseComponent { protected exerciseService = inject(ExerciseService); diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise.service.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise.service.ts index 91a252bd6eb3..6a3dfb64b5c9 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-exercise.service.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-exercise.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -17,13 +17,11 @@ export type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class QuizExerciseService { + private http = inject(HttpClient); + private exerciseService = inject(ExerciseService); + private fileService = inject(FileService); private resourceUrl = 'api/quiz-exercises'; - constructor( - private http: HttpClient, - private exerciseService: ExerciseService, - ) {} - /** * Create the given quiz exercise * @param quizExercise the quiz exercise that should be created @@ -297,8 +295,7 @@ export class QuizExerciseService { * @param filePath the internal path of the file to be fetched */ async fetchFilePromise(fileName: string, zip: JSZip, filePath: string) { - const fileService = new FileService(this.http); - return fileService + return this.fileService .getFile(filePath) .then((fileResult) => { zip.file(fileName, fileResult); diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-management.module.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-management.module.ts index 829eae9fbb76..a2cbf264690a 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-management.module.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-management.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core'; -import { MultipleChoiceQuestionEditComponent } from './multiple-choice-question/multiple-choice-question-edit.component'; -import { DragAndDropQuestionEditComponent } from './drag-and-drop-question/drag-and-drop-question-edit.component'; -import { ShortAnswerQuestionEditComponent } from './short-answer-question/short-answer-question-edit.component'; +import { MultipleChoiceQuestionEditComponent } from 'app/exercises/quiz/manage/multiple-choice-question/multiple-choice-question-edit.component'; +import { DragAndDropQuestionEditComponent } from 'app/exercises/quiz/manage/drag-and-drop-question/drag-and-drop-question-edit.component'; +import { ShortAnswerQuestionEditComponent } from 'app/exercises/quiz/manage/short-answer-question/short-answer-question-edit.component'; import { ArtemisMarkdownEditorModule } from 'app/shared/markdown-editor/markdown-editor.module'; import { DragDropModule } from '@angular/cdk/drag-drop'; import { QuizScoringInfoModalComponent } from './quiz-scoring-info-modal/quiz-scoring-info-modal.component'; @@ -27,7 +27,7 @@ import { QuizConfirmImportInvalidQuestionsModalComponent } from 'app/exercises/q import { ArtemisIncludedInOverallScorePickerModule } from 'app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.module'; import { MatchPercentageInfoModalComponent } from 'app/exercises/quiz/manage/match-percentage-info-modal/match-percentage-info-modal.component'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; -import { FitTextModule } from 'app/exercises/quiz/shared/fit-text/fit-text.module'; + import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ExerciseCategoriesModule } from 'app/shared/exercise-categories/exercise-categories.module'; import { QuizPoolMappingComponent } from 'app/exercises/quiz/manage/quiz-pool-mapping.component'; @@ -59,14 +59,11 @@ const ENTITY_STATES = [...quizManagementRoute]; ArtemisIncludedInOverallScorePickerModule, ArtemisQuizParticipationModule, ArtemisMarkdownModule, - FitTextModule, ArtemisSharedComponentModule, ExerciseCategoriesModule, ExerciseTitleChannelNameModule, DetailModule, ArtemisExerciseModule, - ], - declarations: [ QuizExerciseManageButtonsComponent, QuizExerciseComponent, QuizExerciseCreateButtonsComponent, diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-management.route.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-management.route.ts index 8a7967a5c9c1..f80fb864a44b 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-management.route.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-management.route.ts @@ -1,12 +1,9 @@ import { Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { QuizExerciseUpdateComponent } from './quiz-exercise-update.component'; -import { QuizExerciseExportComponent } from './quiz-exercise-export.component'; + import { PendingChangesGuard } from 'app/shared/guard/pending-changes.guard'; -import { QuizReEvaluateComponent } from 'app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.component'; -import { QuizParticipationComponent } from 'app/exercises/quiz/participate/quiz-participation.component'; + import { Authority } from 'app/shared/constants/authority.constants'; -import { QuizExerciseDetailComponent } from 'app/exercises/quiz/manage/quiz-exercise-detail.component'; export const quizManagementRoute: Routes = [ { @@ -15,7 +12,7 @@ export const quizManagementRoute: Routes = [ }, { path: ':courseId/quiz-exercises/new', - component: QuizExerciseUpdateComponent, + loadComponent: () => import('./quiz-exercise-update.component').then((m) => m.QuizExerciseUpdateComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -25,7 +22,7 @@ export const quizManagementRoute: Routes = [ }, { path: ':courseId/quiz-exercises/export', - component: QuizExerciseExportComponent, + loadComponent: () => import('./quiz-exercise-export.component').then((m) => m.QuizExerciseExportComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -34,7 +31,7 @@ export const quizManagementRoute: Routes = [ }, { path: ':courseId/quiz-exercises/:exerciseId', - component: QuizExerciseDetailComponent, + loadComponent: () => import('app/exercises/quiz/manage/quiz-exercise-detail.component').then((m) => m.QuizExerciseDetailComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -43,7 +40,7 @@ export const quizManagementRoute: Routes = [ }, { path: ':courseId/quiz-exercises/:exerciseId/re-evaluate', - component: QuizReEvaluateComponent, + loadComponent: () => import('app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.component').then((m) => m.QuizReEvaluateComponent), data: { authorities: [Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -52,7 +49,7 @@ export const quizManagementRoute: Routes = [ }, { path: ':courseId/quiz-exercises/:exerciseId/edit', - component: QuizExerciseUpdateComponent, + loadComponent: () => import('./quiz-exercise-update.component').then((m) => m.QuizExerciseUpdateComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -62,7 +59,7 @@ export const quizManagementRoute: Routes = [ }, { path: ':courseId/quiz-exercises/:exerciseId/import', - component: QuizExerciseUpdateComponent, + loadComponent: () => import('./quiz-exercise-update.component').then((m) => m.QuizExerciseUpdateComponent), data: { authorities: [Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.importLabel', @@ -72,7 +69,7 @@ export const quizManagementRoute: Routes = [ }, { path: ':courseId/quiz-exercises/:exerciseId/preview', - component: QuizParticipationComponent, + loadComponent: () => import('app/exercises/quiz/participate/quiz-participation.component').then((m) => m.QuizParticipationComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', @@ -82,7 +79,7 @@ export const quizManagementRoute: Routes = [ }, { path: ':courseId/quiz-exercises/:exerciseId/solution', - component: QuizParticipationComponent, + loadComponent: () => import('app/exercises/quiz/participate/quiz-participation.component').then((m) => m.QuizParticipationComponent), data: { authorities: [Authority.TA, Authority.EDITOR, Authority.INSTRUCTOR, Authority.ADMIN], pageTitle: 'artemisApp.quizExercise.home.title', diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-pool-mapping-question-list.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-pool-mapping-question-list.component.ts index 11f72daf4686..23d73f2b23cd 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-pool-mapping-question-list.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-pool-mapping-question-list.component.ts @@ -1,13 +1,14 @@ import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; import { QuizQuestion } from 'app/entities/quiz/quiz-question.model'; import { QuizQuestionType } from 'app/entities/quiz/quiz-question.model'; -import { CdkDragDrop, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDropList, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop'; @Component({ selector: 'jhi-quiz-pool-mapping-question-list', templateUrl: './quiz-pool-mapping-question-list.component.html', styleUrls: ['./quiz-pool-mapping-question-list.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [CdkDropList, CdkDrag], }) export class QuizPoolMappingQuestionListComponent { @Input() quizQuestions: Array; diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-pool-mapping.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-pool-mapping.component.ts index e4fd431ab46f..abb33d0ab04e 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-pool-mapping.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-pool-mapping.component.ts @@ -1,16 +1,25 @@ -import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, inject } from '@angular/core'; import { faExclamationCircle, faPlus, faTimes } from '@fortawesome/free-solid-svg-icons'; import { QuizGroup } from 'app/entities/quiz/quiz-group.model'; import { Subject } from 'rxjs'; import { QuizQuestion } from 'app/entities/quiz/quiz-question.model'; import { AlertService } from 'app/core/util/alert.service'; +import { CdkDropListGroup } from '@angular/cdk/drag-drop'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { QuizPoolMappingQuestionListComponent } from './quiz-pool-mapping-question-list.component'; +import { DeleteButtonDirective } from 'app/shared/delete-dialog/delete-button.directive'; +import { NgStyle } from '@angular/common'; @Component({ selector: 'jhi-quiz-pool-mapping', templateUrl: './quiz-pool-mapping.component.html', styleUrls: ['./quiz-pool-mapping.component.scss'], + imports: [CdkDropListGroup, FaIconComponent, TranslateDirective, QuizPoolMappingQuestionListComponent, DeleteButtonDirective, NgStyle], }) export class QuizPoolMappingComponent implements OnInit, OnChanges, OnDestroy { + private alertService = inject(AlertService); + @Input() quizGroups: QuizGroup[] = []; @Input() quizQuestions: QuizQuestion[] = []; @Input() disabled = false; @@ -28,13 +37,11 @@ export class QuizPoolMappingComponent implements OnInit, OnChanges, OnDestroy { protected dialogErrorSource = new Subject(); dialogError$ = this.dialogErrorSource.asObservable(); - constructor(private alertService: AlertService) {} - ngOnInit(): void { this.handleUpdate(); } - ngOnChanges(): void { + ngOnChanges() { this.handleUpdate(); } diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-pool.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-pool.component.ts index ea04a7e2fe7f..85ee99f058e4 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-pool.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-pool.component.ts @@ -1,5 +1,5 @@ import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { QuizPool } from 'app/entities/quiz/quiz-pool.model'; import { QuizPoolService } from 'app/exercises/quiz/manage/quiz-pool.service'; @@ -17,6 +17,11 @@ import { computeQuizQuestionInvalidReason, isQuizQuestionValid } from 'app/exerc import { ExamManagementService } from 'app/exam/manage/exam-management.service'; import { Exam } from 'app/entities/exam/exam.model'; import dayjs from 'dayjs/esm'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { JsonPipe } from '@angular/common'; @Component({ selector: 'jhi-quiz-pool', @@ -25,8 +30,17 @@ import dayjs from 'dayjs/esm'; changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: ['./quiz-pool.component.scss', '../shared/quiz.scss'], encapsulation: ViewEncapsulation.None, + imports: [TranslateDirective, QuizPoolMappingComponent, FormsModule, QuizQuestionListEditComponent, NgbTooltip, FaIconComponent, JsonPipe], }) export class QuizPoolComponent implements OnInit { + private route = inject(ActivatedRoute); + private quizPoolService = inject(QuizPoolService); + private examService = inject(ExamManagementService); + private changeDetectorRef = inject(ChangeDetectorRef); + private dragAndDropQuestionUtil = inject(DragAndDropQuestionUtil); + private shortAnswerQuestionUtil = inject(ShortAnswerQuestionUtil); + private alertService = inject(AlertService); + @ViewChild('quizPoolQuestionMapping') quizPoolMappingComponent: QuizPoolMappingComponent; @ViewChild('quizQuestionsEdit') @@ -46,16 +60,6 @@ export class QuizPoolComponent implements OnInit { examId: number; isExamStarted: boolean; - constructor( - private route: ActivatedRoute, - private quizPoolService: QuizPoolService, - private examService: ExamManagementService, - private changeDetectorRef: ChangeDetectorRef, - private dragAndDropQuestionUtil: DragAndDropQuestionUtil, - private shortAnswerQuestionUtil: ShortAnswerQuestionUtil, - private alertService: AlertService, - ) {} - ngOnInit(): void { this.courseId = Number(this.route.snapshot.paramMap.get('courseId')); this.examId = Number(this.route.snapshot.paramMap.get('examId')); diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-pool.service.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-pool.service.ts index c1b8e7cf4fae..f0c8944dafc1 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-pool.service.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-pool.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; @@ -10,7 +10,7 @@ export type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class QuizPoolService { - constructor(private http: HttpClient) {} + private http = inject(HttpClient); /** * Update the given quiz pool that belongs to the given course id and exam id diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit-existing.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit-existing.component.ts index 7dc2df7ed5d3..3a5689b8be8e 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit-existing.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit-existing.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, Output, ViewEncapsulation, inject } from '@angular/core'; import { QuizQuestion, QuizQuestionType } from 'app/entities/quiz/quiz-question.model'; import { DragAndDropQuestion } from 'app/entities/quiz/drag-and-drop-question.model'; import { MultipleChoiceQuestion } from 'app/entities/quiz/multiple-choice-question.model'; @@ -17,6 +17,9 @@ import { onError } from 'app/shared/util/global.utils'; import { checkForInvalidFlaggedQuestions } from 'app/exercises/quiz/shared/quiz-manage-util.service'; import { FileService } from 'app/shared/http/file.service'; import JSZip from 'jszip'; +import { KeyValuePipe, NgClass } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; export enum State { COURSE = 'Course', @@ -30,8 +33,17 @@ export enum State { changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: ['../shared/quiz.scss'], encapsulation: ViewEncapsulation.None, + imports: [NgClass, TranslateDirective, FormsModule, KeyValuePipe], }) export class QuizQuestionListEditExistingComponent implements OnChanges { + private modalService = inject(NgbModal); + private fileService = inject(FileService); + private courseManagementService = inject(CourseManagementService); + private examManagementService = inject(ExamManagementService); + private quizExerciseService = inject(QuizExerciseService); + private alertService = inject(AlertService); + private changeDetectorRef = inject(ChangeDetectorRef); + @Input() show: boolean; @Input() courseId: number; @Input() filePool: Map; @@ -59,17 +71,7 @@ export class QuizQuestionListEditExistingComponent implements OnChanges { importFile?: File; importFileName: string; - constructor( - private modalService: NgbModal, - private fileService: FileService, - private courseManagementService: CourseManagementService, - private examManagementService: ExamManagementService, - private quizExerciseService: QuizExerciseService, - private alertService: AlertService, - private changeDetectorRef: ChangeDetectorRef, - ) {} - - ngOnChanges(): void { + ngOnChanges() { if (this.show) { this.courseManagementService.getAllCoursesWithQuizExercises().subscribe((res: HttpResponse) => { this.courses = res.body!; diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit.component.ts index 3d3da811b347..bff74eb3afd8 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-question-list-edit.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, QueryList, ViewChildren, ViewEncapsulation, inject } from '@angular/core'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; import { QuizQuestion, QuizQuestionType, ScoringType } from 'app/entities/quiz/quiz-question.model'; import { MultipleChoiceQuestion } from 'app/entities/quiz/multiple-choice-question.model'; @@ -11,6 +11,10 @@ import { MultipleChoiceQuestionEditComponent } from 'app/exercises/quiz/manage/m import { DragAndDropQuestionEditComponent } from 'app/exercises/quiz/manage/drag-and-drop-question/drag-and-drop-question-edit.component'; import { ShortAnswerQuestionEditComponent } from 'app/exercises/quiz/manage/short-answer-question/short-answer-question-edit.component'; import { ApollonDiagramImportDialogComponent } from 'app/exercises/quiz/manage/apollon-diagrams/apollon-diagram-import-dialog.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass } from '@angular/common'; +import { QuizQuestionListEditExistingComponent } from './quiz-question-list-edit-existing.component'; @Component({ selector: 'jhi-quiz-question-list-edit', @@ -18,8 +22,19 @@ import { ApollonDiagramImportDialogComponent } from 'app/exercises/quiz/manage/a changeDetection: ChangeDetectionStrategy.OnPush, styleUrls: ['./quiz-question-list-edit.component.scss', '../shared/quiz.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + TranslateDirective, + MultipleChoiceQuestionEditComponent, + DragAndDropQuestionEditComponent, + ShortAnswerQuestionEditComponent, + FaIconComponent, + NgClass, + QuizQuestionListEditExistingComponent, + ], }) export class QuizQuestionListEditComponent { + private modalService = inject(NgbModal); + @Input() courseId: number; @Input() quizQuestions: QuizQuestion[] = []; @Input() disabled = false; @@ -47,8 +62,6 @@ export class QuizQuestionListEditComponent { fileMap = new Map(); - constructor(private modalService: NgbModal) {} - /** * Emit onQuestionUpdated if there is an update of the question. */ diff --git a/src/main/webapp/app/exercises/quiz/manage/quiz-scoring-info-modal/quiz-scoring-info-modal.component.ts b/src/main/webapp/app/exercises/quiz/manage/quiz-scoring-info-modal/quiz-scoring-info-modal.component.ts index d362837455f4..18d21089f3b9 100644 --- a/src/main/webapp/app/exercises/quiz/manage/quiz-scoring-info-modal/quiz-scoring-info-modal.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/quiz-scoring-info-modal/quiz-scoring-info-modal.component.ts @@ -1,15 +1,19 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { faQuestionCircle } from '@fortawesome/free-regular-svg-icons'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-quiz-scoring-info-modal', templateUrl: './quiz-scoring-info-modal.component.html', + imports: [TranslateDirective, FaIconComponent], }) export class QuizScoringInfoModalComponent { + private modalService = inject(NgbModal); + // Icons farQuestionCircle = faQuestionCircle; - constructor(private modalService: NgbModal) {} /** * Open a large modal with the given content. diff --git a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/drag-and-drop-question/re-evaluate-drag-and-drop-question.component.ts b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/drag-and-drop-question/re-evaluate-drag-and-drop-question.component.ts index 1fd06c312206..0db02d8ebd73 100644 --- a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/drag-and-drop-question/re-evaluate-drag-and-drop-question.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/drag-and-drop-question/re-evaluate-drag-and-drop-question.component.ts @@ -19,6 +19,7 @@ import { DragAndDropQuestionEditComponent } from 'app/exercises/quiz/manage/drag /> `, providers: [], + imports: [DragAndDropQuestionEditComponent], }) export class ReEvaluateDragAndDropQuestionComponent { /** @@ -49,8 +50,6 @@ export class ReEvaluateDragAndDropQuestionComponent { fileMap = new Map(); - constructor() {} - /** * Add the given file to the fileMap for later upload. * @param event the event containing the file and its name. The name provided may be different from the actual file name but has to correspond to the name set in the entity object. diff --git a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/multiple-choice-question/re-evaluate-multiple-choice-question.component.ts b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/multiple-choice-question/re-evaluate-multiple-choice-question.component.ts index a9a0afae1077..fd1ddcf39660 100644 --- a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/multiple-choice-question/re-evaluate-multiple-choice-question.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/multiple-choice-question/re-evaluate-multiple-choice-question.component.ts @@ -5,14 +5,21 @@ import { escapeStringForUseInRegex } from 'app/shared/util/global.utils'; import { cloneDeep } from 'lodash-es'; import { generateExerciseHintExplanation, parseExerciseHintExplanation } from 'app/shared/util/markdown.util'; import { faAngleDown, faAngleRight, faArrowsAltV, faChevronDown, faChevronUp, faTrash, faUndo } from '@fortawesome/free-solid-svg-icons'; -import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { CdkDrag, CdkDragDrop, CdkDragHandle, CdkDropList, moveItemInArray } from '@angular/cdk/drag-drop'; import { CorrectMultipleChoiceAnswerAction } from 'app/shared/monaco-editor/model/actions/quiz/correct-multiple-choice-answer.action'; import { WrongMultipleChoiceAnswerAction } from 'app/shared/monaco-editor/model/actions/quiz/wrong-multiple-choice-answer.action'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormsModule } from '@angular/forms'; +import { NgbCollapse, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { MarkdownEditorMonacoComponent } from 'app/shared/markdown-editor/monaco/markdown-editor-monaco.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-re-evaluate-multiple-choice-question', templateUrl: './re-evaluate-multiple-choice-question.component.html', styleUrls: ['./re-evaluate-multiple-choice-question.component.scss', '../../../shared/quiz.scss'], + imports: [FaIconComponent, FormsModule, NgbTooltip, NgbCollapse, TranslateDirective, MarkdownEditorMonacoComponent, CdkDropList, CdkDrag, CdkDragHandle, ArtemisTranslatePipe], }) export class ReEvaluateMultipleChoiceQuestionComponent implements OnInit { @Input() question: MultipleChoiceQuestion; diff --git a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate-warning.component.ts b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate-warning.component.ts index 6947cdbb1635..63ef0a60b647 100644 --- a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate-warning.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate-warning.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, inject } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { QuizReEvaluateService } from './quiz-re-evaluate.service'; import { ShortAnswerQuestion } from 'app/entities/quiz/short-answer-question.model'; @@ -10,13 +10,22 @@ import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { EventManager } from 'app/core/util/event-manager.service'; import { faBan, faCheck, faCheckCircle, faSpinner, faTimes } from '@fortawesome/free-solid-svg-icons'; import { ArtemisNavigationUtilService } from 'app/utils/navigation.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; @Component({ selector: 'jhi-quiz-re-evaluate-warning', templateUrl: './quiz-re-evaluate-warning.component.html', styleUrls: ['../../shared/quiz.scss'], + imports: [TranslateDirective, FaIconComponent], }) export class QuizReEvaluateWarningComponent implements OnInit { + activeModal = inject(NgbActiveModal); + private eventManager = inject(EventManager); + private quizExerciseService = inject(QuizExerciseService); + private quizReEvaluateService = inject(QuizReEvaluateService); + private navigationUtilService = inject(ArtemisNavigationUtilService); + isSaving: boolean; successful = false; @@ -43,14 +52,6 @@ export class QuizReEvaluateWarningComponent implements OnInit { faCheck = faCheck; faCheckCircle = faCheckCircle; - constructor( - public activeModal: NgbActiveModal, - private eventManager: EventManager, - private quizExerciseService: QuizExerciseService, - private quizReEvaluateService: QuizReEvaluateService, - private navigationUtilService: ArtemisNavigationUtilService, - ) {} - /** * Reset saving status, load the quiz by id and back it up. */ diff --git a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.component.ts b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.component.ts index 22525d9e37a1..518e50f49ca0 100644 --- a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.component.ts @@ -1,7 +1,8 @@ -import { ChangeDetectorRef, Component, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChildren, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectorRef, Component, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChildren, ViewEncapsulation, inject } from '@angular/core'; +import { IncludedInOverallScorePickerComponent } from 'app/exercises/shared/included-in-overall-score-picker/included-in-overall-score-picker.component'; import { Subscription } from 'rxjs'; import { ActivatedRoute } from '@angular/router'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { QuizReEvaluateWarningComponent } from './quiz-re-evaluate-warning.component'; import { DragAndDropQuestionUtil } from 'app/exercises/quiz/shared/drag-and-drop-question-util.service'; import { HttpResponse } from '@angular/common/http'; @@ -18,6 +19,14 @@ import { QuizExerciseValidationDirective } from 'app/exercises/quiz/manage/quiz- import { ShortAnswerQuestionUtil } from 'app/exercises/quiz/shared/short-answer-question-util.service'; import { faExclamationCircle, faExclamationTriangle, faUndo } from '@fortawesome/free-solid-svg-icons'; import { ReEvaluateDragAndDropQuestionComponent } from 'app/exercises/quiz/manage/re-evaluate/drag-and-drop-question/re-evaluate-drag-and-drop-question.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormsModule } from '@angular/forms'; +import { FormDateTimePickerComponent } from 'app/shared/date-time-picker/date-time-picker.component'; +import { ReEvaluateMultipleChoiceQuestionComponent } from './multiple-choice-question/re-evaluate-multiple-choice-question.component'; +import { ReEvaluateShortAnswerQuestionComponent } from './short-answer-question/re-evaluate-short-answer-question.component'; +import { JsonPipe } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-quiz-re-evaluate', @@ -25,8 +34,28 @@ import { ReEvaluateDragAndDropQuestionComponent } from 'app/exercises/quiz/manag styleUrls: ['./quiz-re-evaluate.component.scss', '../../shared/quiz.scss'], encapsulation: ViewEncapsulation.None, providers: [DragAndDropQuestionUtil, ShortAnswerQuestionUtil], + imports: [ + TranslateDirective, + FaIconComponent, + FormsModule, + NgbTooltip, + FormDateTimePickerComponent, + IncludedInOverallScorePickerComponent, + ReEvaluateMultipleChoiceQuestionComponent, + ReEvaluateDragAndDropQuestionComponent, + ReEvaluateShortAnswerQuestionComponent, + JsonPipe, + ArtemisTranslatePipe, + ], }) export class QuizReEvaluateComponent extends QuizExerciseValidationDirective implements OnInit, OnChanges, OnDestroy { + private quizExerciseService = inject(QuizExerciseService); + private route = inject(ActivatedRoute); + private modalServiceC = inject(NgbModal); + private quizExercisePopupService = inject(QuizExercisePopupService); + private changeDetector = inject(ChangeDetectorRef); + private navigationUtilService = inject(ArtemisNavigationUtilService); + private subscription: Subscription; @ViewChildren(ReEvaluateDragAndDropQuestionComponent) @@ -43,19 +72,6 @@ export class QuizReEvaluateComponent extends QuizExerciseValidationDirective imp faExclamationCircle = faExclamationCircle; faExclamationTriangle = faExclamationTriangle; - constructor( - private quizExerciseService: QuizExerciseService, - private route: ActivatedRoute, - private modalServiceC: NgbModal, - private quizExercisePopupService: QuizExercisePopupService, - public changeDetector: ChangeDetectorRef, - private navigationUtilService: ArtemisNavigationUtilService, - dragAndDropQuestionUtil: DragAndDropQuestionUtil, - shortAnswerQuestionUtil: ShortAnswerQuestionUtil, - ) { - super(dragAndDropQuestionUtil, shortAnswerQuestionUtil); - } - ngOnInit(): void { this.subscription = this.route.params.subscribe((params) => { this.quizExerciseService.find(params['exerciseId']).subscribe((response: HttpResponse) => { diff --git a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.service.ts b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.service.ts index 8a863a6906b7..4c43bc6b6fcf 100644 --- a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.service.ts +++ b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/quiz-re-evaluate.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service'; @@ -6,9 +6,9 @@ import { objectToJsonBlob } from 'app/utils/blob-util'; @Injectable({ providedIn: 'root' }) export class QuizReEvaluateService { - private resourceUrl = 'api/quiz-exercises/'; + private http = inject(HttpClient); - constructor(private http: HttpClient) {} + private resourceUrl = 'api/quiz-exercises/'; reevaluate(quizExercise: QuizExercise, files: Map) { const copy = this.convert(quizExercise); diff --git a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/short-answer-question/re-evaluate-short-answer-question.component.ts b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/short-answer-question/re-evaluate-short-answer-question.component.ts index 136135c58221..289b20cace34 100644 --- a/src/main/webapp/app/exercises/quiz/manage/re-evaluate/short-answer-question/re-evaluate-short-answer-question.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/re-evaluate/short-answer-question/re-evaluate-short-answer-question.component.ts @@ -1,6 +1,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { QuizQuestion } from 'app/entities/quiz/quiz-question.model'; import { ShortAnswerQuestion } from 'app/entities/quiz/short-answer-question.model'; +import { ShortAnswerQuestionEditComponent } from '../../short-answer-question/short-answer-question-edit.component'; @Component({ selector: 'jhi-re-evaluate-short-answer-question', @@ -16,6 +17,7 @@ import { ShortAnswerQuestion } from 'app/entities/quiz/short-answer-question.mod /> `, providers: [], + imports: [ShortAnswerQuestionEditComponent], }) export class ReEvaluateShortAnswerQuestionComponent { shortAnswerQuestion: ShortAnswerQuestion; @@ -34,6 +36,4 @@ export class ReEvaluateShortAnswerQuestionComponent { questionMoveUp = new EventEmitter(); @Output() questionMoveDown = new EventEmitter(); - - constructor() {} } diff --git a/src/main/webapp/app/exercises/quiz/manage/short-answer-question/short-answer-question-edit.component.ts b/src/main/webapp/app/exercises/quiz/manage/short-answer-question/short-answer-question-edit.component.ts index 896a6c743bbe..b71f3010f050 100644 --- a/src/main/webapp/app/exercises/quiz/manage/short-answer-question/short-answer-question-edit.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/short-answer-question/short-answer-question-edit.component.ts @@ -11,9 +11,10 @@ import { SimpleChanges, ViewChild, ViewEncapsulation, + inject, } from '@angular/core'; import { ShortAnswerQuestionUtil } from 'app/exercises/quiz/shared/short-answer-question-util.service'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { NgbCollapse, NgbModal, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; import { ShortAnswerQuestion } from 'app/entities/quiz/short-answer-question.model'; import { ShortAnswerMapping } from 'app/entities/quiz/short-answer-mapping.model'; import { QuizQuestionEdit } from 'app/exercises/quiz/manage/quiz-question-edit.interface'; @@ -38,20 +39,45 @@ import { InsertShortAnswerSpotAction } from 'app/shared/monaco-editor/model/acti import { TextEditorAction } from 'app/shared/monaco-editor/model/actions/text-editor-action.model'; import { InsertShortAnswerOptionAction } from 'app/shared/monaco-editor/model/actions/quiz/insert-short-answer-option.action'; import { SHORT_ANSWER_QUIZ_QUESTION_EDITOR_OPTIONS } from 'app/shared/monaco-editor/monaco-editor-option.helper'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { FormsModule } from '@angular/forms'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { QuizScoringInfoModalComponent } from '../quiz-scoring-info-modal/quiz-scoring-info-modal.component'; +import { MatchPercentageInfoModalComponent } from '../match-percentage-info-modal/match-percentage-info-modal.component'; +import { CdkDrag, CdkDragPlaceholder, CdkDragPreview, CdkDropList, CdkDropListGroup } from '@angular/cdk/drag-drop'; +import { NgClass } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-short-answer-question-edit', templateUrl: './short-answer-question-edit.component.html', styleUrls: ['./short-answer-question-edit.component.scss', '../quiz-exercise.scss', '../../shared/quiz.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + FaIconComponent, + FormsModule, + TranslateDirective, + NgbTooltip, + NgbCollapse, + QuizScoringInfoModalComponent, + MatchPercentageInfoModalComponent, + MarkdownEditorMonacoComponent, + CdkDropListGroup, + CdkDropList, + NgClass, + CdkDrag, + CdkDragPlaceholder, + CdkDragPreview, + ArtemisTranslatePipe, + ], }) export class ShortAnswerQuestionEditComponent implements OnInit, OnChanges, AfterViewInit, QuizQuestionEdit { - @ViewChild('questionEditor', { static: false }) - private questionEditor: MarkdownEditorMonacoComponent; - @ViewChild('clickLayer', { static: false }) - private clickLayer: ElementRef; - @ViewChild('question', { static: false }) - questionElement: ElementRef; + shortAnswerQuestionUtil = inject(ShortAnswerQuestionUtil); + private modalService = inject(NgbModal); + private changeDetector = inject(ChangeDetectorRef); + + @ViewChild('questionEditor', { static: false }) private questionEditor: MarkdownEditorMonacoComponent; + @ViewChild('question', { static: false }) questionElement: ElementRef; markdownActions: TextEditorAction[]; insertShortAnswerOptionAction = new InsertShortAnswerOptionAction(); @@ -112,12 +138,6 @@ export class ShortAnswerQuestionEditComponent implements OnInit, OnChanges, Afte protected readonly MAX_POINTS = MAX_QUIZ_QUESTION_POINTS; protected readonly MarkdownEditorHeight = MarkdownEditorHeight; - constructor( - public shortAnswerQuestionUtil: ShortAnswerQuestionUtil, - private modalService: NgbModal, - private changeDetector: ChangeDetectorRef, - ) {} - ngOnInit(): void { this.markdownActions = [ new BoldAction(), @@ -137,7 +157,7 @@ export class ShortAnswerQuestionEditComponent implements OnInit, OnChanges, Afte /** We create now the structure on how to display the text of the question * 1. The question text is split at every new line. The first element of the array would be then the first line of the question text. - * 2. Now each line of the question text will be divided into each word (we use whitespace and the borders of spots as separator, see {@link #regex}). + * 2. Now each line of the question text will be divided into each word (we use whitespace and the borders of spots as separator, see regex). */ this.textParts = this.parseQuestionTextIntoTextBlocks(this.shortAnswerQuestion.text!); diff --git a/src/main/webapp/app/exercises/quiz/manage/statistics/drag-and-drop-question-statistic/drag-and-drop-question-statistic.component.ts b/src/main/webapp/app/exercises/quiz/manage/statistics/drag-and-drop-question-statistic/drag-and-drop-question-statistic.component.ts index 2e0208207eeb..6965c21f0f58 100644 --- a/src/main/webapp/app/exercises/quiz/manage/statistics/drag-and-drop-question-statistic/drag-and-drop-question-statistic.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/statistics/drag-and-drop-question-statistic/drag-and-drop-question-statistic.component.ts @@ -1,18 +1,20 @@ -import { ChangeDetectorRef, Component, ViewEncapsulation } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; +import { Component, ViewEncapsulation, inject } from '@angular/core'; +import { DragItemComponent } from 'app/exercises/quiz/shared/questions/drag-and-drop-question/drag-item.component'; import { QuizStatisticUtil } from 'app/exercises/quiz/shared/quiz-statistic-util.service'; import { DragAndDropQuestionUtil } from 'app/exercises/quiz/shared/drag-and-drop-question-util.service'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; -import { AccountService } from 'app/core/auth/account.service'; -import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; -import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.service'; import { DragAndDropQuestion } from 'app/entities/quiz/drag-and-drop-question.model'; import { DragAndDropQuestionStatistic } from 'app/entities/quiz/drag-and-drop-question-statistic.model'; import { DropLocation } from 'app/entities/quiz/drop-location.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { QuestionStatisticComponent, blueColor, greenColor } from 'app/exercises/quiz/manage/statistics/question-statistic.component'; import { faCheckCircle, faSync, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { BarChartModule } from '@swimlane/ngx-charts'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { SecuredImageComponent } from 'app/shared/image/secured-image.component'; +import { NgStyle } from '@angular/common'; +import { QuizStatisticsFooterComponent } from '../quiz-statistics-footer/quiz-statistics-footer.component'; @Component({ selector: 'jhi-drag-and-drop-question-statistic', @@ -24,8 +26,12 @@ import { faCheckCircle, faSync, faTimesCircle } from '@fortawesome/free-solid-sv './drag-and-drop-question-statistic.component.scss', ], encapsulation: ViewEncapsulation.None, + imports: [TranslateDirective, BarChartModule, FaIconComponent, SecuredImageComponent, NgStyle, DragItemComponent, QuizStatisticsFooterComponent], }) export class DragAndDropQuestionStatisticComponent extends QuestionStatisticComponent { + private dragAndDropQuestionUtil = inject(DragAndDropQuestionUtil); + private artemisMarkdown = inject(ArtemisMarkdownService); + declare question: DragAndDropQuestion; // Icons @@ -33,27 +39,6 @@ export class DragAndDropQuestionStatisticComponent extends QuestionStatisticComp faCheckCircle = faCheckCircle; faTimesCircle = faTimesCircle; - constructor( - route: ActivatedRoute, - router: Router, - accountService: AccountService, - translateService: TranslateService, - quizExerciseService: QuizExerciseService, - jhiWebsocketService: JhiWebsocketService, - quizStatisticUtil: QuizStatisticUtil, - private dragAndDropQuestionUtil: DragAndDropQuestionUtil, - private artemisMarkdown: ArtemisMarkdownService, - protected changeDetector: ChangeDetectorRef, - ) { - super(route, router, accountService, translateService, quizExerciseService, jhiWebsocketService, changeDetector); - } - - /** - * load the Quiz, which is necessary to build the Web-Template - * - * @param {QuizExercise} quiz: the quizExercise, which the selected question is part of. - * @param {boolean} refresh: true if method is called from Websocket - */ loadQuiz(quiz: QuizExercise, refresh: boolean) { const updatedQuestion = super.loadQuizCommon(quiz); if (!updatedQuestion) { @@ -75,7 +60,7 @@ export class DragAndDropQuestionStatisticComponent extends QuestionStatisticComp this.resetLabelsColors(); // set label and background color based on the dropLocations - this.question.dropLocations!.forEach((dropLocation, i) => { + this.question.dropLocations!.forEach((_dropLocation, i) => { this.labels.push(this.getLetter(i) + '.'); this.solutionLabels.push(this.getLetter(i) + '.'); this.backgroundColors.push(blueColor); @@ -127,8 +112,8 @@ export class DragAndDropQuestionStatisticComponent extends QuestionStatisticComp /** * Get the drag item that was mapped to the given drop location in the sample solution * - * @param dropLocation {object} the drop location that the drag item should be mapped to - * @return {DragItem | undefined} the mapped drag item, or undefined if no drag item has been mapped to this location + * @param dropLocation the drop location that the drag item should be mapped to + * @return the mapped drag item, or undefined if no drag item has been mapped to this location */ correctDragItemForDropLocation(dropLocation: DropLocation) { const currMapping = this.dragAndDropQuestionUtil.solve(this.question, undefined).filter((mapping) => mapping.dropLocation!.id === dropLocation.id)[0]; diff --git a/src/main/webapp/app/exercises/quiz/manage/statistics/multiple-choice-question-statistic/multiple-choice-question-statistic.component.ts b/src/main/webapp/app/exercises/quiz/manage/statistics/multiple-choice-question-statistic/multiple-choice-question-statistic.component.ts index b8071ce5823e..d294491f4881 100644 --- a/src/main/webapp/app/exercises/quiz/manage/statistics/multiple-choice-question-statistic/multiple-choice-question-statistic.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/statistics/multiple-choice-question-statistic/multiple-choice-question-statistic.component.ts @@ -1,25 +1,26 @@ -import { ChangeDetectorRef, Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; import { SafeHtml } from '@angular/platform-browser'; -import { ActivatedRoute, Router } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; import { QuizStatisticUtil } from 'app/exercises/quiz/shared/quiz-statistic-util.service'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; -import { AccountService } from 'app/core/auth/account.service'; -import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; -import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.service'; import { MultipleChoiceQuestionStatistic } from 'app/entities/quiz/multiple-choice-question-statistic.model'; import { MultipleChoiceQuestion } from 'app/entities/quiz/multiple-choice-question.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { QuestionStatisticComponent, blueColor, greenColor, redColor } from 'app/exercises/quiz/manage/statistics/question-statistic.component'; import { faCheckCircle, faSync, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { BarChartModule } from '@swimlane/ngx-charts'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { QuizStatisticsFooterComponent } from '../quiz-statistics-footer/quiz-statistics-footer.component'; @Component({ selector: 'jhi-multiple-choice-question-statistic', templateUrl: './multiple-choice-question-statistic.component.html', styleUrls: ['../quiz-point-statistic/quiz-point-statistic.component.scss', '../../../../../shared/chart/vertical-bar-chart.scss'], providers: [QuizStatisticUtil], + imports: [TranslateDirective, BarChartModule, FaIconComponent, QuizStatisticsFooterComponent], }) export class MultipleChoiceQuestionStatisticComponent extends QuestionStatisticComponent { + private artemisMarkdown = inject(ArtemisMarkdownService); declare question: MultipleChoiceQuestion; answerTextRendered: SafeHtml[]; @@ -29,26 +30,6 @@ export class MultipleChoiceQuestionStatisticComponent extends QuestionStatisticC faCheckCircle = faCheckCircle; faTimesCircle = faTimesCircle; - constructor( - route: ActivatedRoute, - router: Router, - accountService: AccountService, - translateService: TranslateService, - quizExerciseService: QuizExerciseService, - jhiWebsocketService: JhiWebsocketService, - private quizStatisticUtil: QuizStatisticUtil, - private artemisMarkdown: ArtemisMarkdownService, - protected changeDetector: ChangeDetectorRef, - ) { - super(route, router, accountService, translateService, quizExerciseService, jhiWebsocketService, changeDetector); - } - - /** - * This functions loads the Quiz, which is necessary to build the Web-Template - * - * @param {QuizExercise} quiz: the quizExercise, which the selected question is part of. - * @param {boolean} refresh: true if method is called from Websocket - */ loadQuiz(quiz: QuizExercise, refresh: boolean) { const updatedQuestion = super.loadQuizCommon(quiz); if (!updatedQuestion) { @@ -73,7 +54,7 @@ export class MultipleChoiceQuestionStatisticComponent extends QuestionStatisticC const answerOptions = this.question.answerOptions!; // set label and background-Color based on the AnswerOptions - answerOptions.forEach((answerOption, i) => { + answerOptions.forEach((_answerOption, i) => { this.labels.push(this.getLetter(i) + '.'); this.backgroundColors.push(blueColor); }); diff --git a/src/main/webapp/app/exercises/quiz/manage/statistics/question-statistic.component.ts b/src/main/webapp/app/exercises/quiz/manage/statistics/question-statistic.component.ts index f483ce7c6059..7b79e4dfb324 100644 --- a/src/main/webapp/app/exercises/quiz/manage/statistics/question-statistic.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/statistics/question-statistic.component.ts @@ -8,10 +8,9 @@ import { Authority } from 'app/shared/constants/authority.constants'; import { Subscription } from 'rxjs'; import { SafeHtml } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; -import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { CanBecomeInvalid } from 'app/entities/quiz/drop-location.model'; -import { QuizStatistics } from 'app/exercises/quiz/manage/statistics/quiz-statistics'; +import { AbstractQuizStatisticComponent } from 'app/exercises/quiz/manage/statistics/quiz-statistics'; export const redColor = '#d9534f'; export const greenColor = '#5cb85c'; @@ -19,8 +18,17 @@ export const blueColor = '#428bca'; export const lightBlueColor = '#5bc0de'; export const greyColor = '#838383'; -@Component({ template: '' }) -export abstract class QuestionStatisticComponent extends QuizStatistics implements OnInit, OnDestroy { +@Component({ + template: '', +}) +export abstract class QuestionStatisticComponent extends AbstractQuizStatisticComponent implements OnInit, OnDestroy { + protected route = inject(ActivatedRoute); + protected router = inject(Router); + protected accountService = inject(AccountService); + protected quizExerciseService = inject(QuizExerciseService); + protected jhiWebsocketService = inject(JhiWebsocketService); + protected changeDetector = inject(ChangeDetectorRef); + question: QuizQuestion; questionStatistic: QuizQuestionStatistic; @@ -45,22 +53,10 @@ export abstract class QuestionStatisticComponent extends QuizStatistics implemen backgroundColors: string[] = []; backgroundSolutionColors: string[] = []; - constructor( - protected route: ActivatedRoute, - protected router: Router, - protected accountService: AccountService, - protected translateService: TranslateService, - protected quizExerciseService: QuizExerciseService, - protected jhiWebsocketService: JhiWebsocketService, - protected changeDetector: ChangeDetectorRef, - ) { - super(translateService); - translateService.onLangChange.subscribe(() => { + ngOnInit() { + this.translateService.onLangChange.subscribe(() => { this.setAxisLabels('showStatistic.questionStatistic.xAxes', 'showStatistic.questionStatistic.yAxes'); }); - } - - ngOnInit() { this.sub = this.route.params.subscribe((params) => { this.questionIdParam = +params['questionId']; // use different REST-call if the User is a Student @@ -132,6 +128,12 @@ export abstract class QuestionStatisticComponent extends QuizStatistics implemen this.loadDataInDiagram(); } + /** + * This functions loads the Quiz, which is necessary to build the Web-Template + * + * @param quiz the quizExercise, which the selected question is part of. + * @param refresh true if method is called from Websocket + */ abstract loadQuiz(quiz: QuizExercise, refresh: boolean): void; loadQuizCommon(quiz: QuizExercise) { diff --git a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-point-statistic/quiz-point-statistic.component.ts b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-point-statistic/quiz-point-statistic.component.ts index bf9877902dfe..bb31e1baee65 100644 --- a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-point-statistic/quiz-point-statistic.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-point-statistic/quiz-point-statistic.component.ts @@ -1,7 +1,6 @@ -import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Subscription } from 'rxjs'; -import { QuizStatisticUtil } from 'app/exercises/quiz/shared/quiz-statistic-util.service'; +import { AbstractQuizStatisticComponent } from 'app/exercises/quiz/manage/statistics/quiz-statistics'; import { AccountService } from 'app/core/auth/account.service'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { PointCounter } from 'app/entities/quiz/point-counter.model'; @@ -12,23 +11,34 @@ import { Authority } from 'app/shared/constants/authority.constants'; import { blueColor } from 'app/exercises/quiz/manage/statistics/question-statistic.component'; import { UI_RELOAD_TIME } from 'app/shared/constants/exercise-exam-constants'; import { round } from 'app/shared/util/utils'; -import { QuizStatistics } from 'app/exercises/quiz/manage/statistics/quiz-statistics'; -import { TranslateService } from '@ngx-translate/core'; import { faSync } from '@fortawesome/free-solid-svg-icons'; import { calculateMaxScore } from 'app/exercises/quiz/manage/statistics/quiz-statistic/quiz-statistics.utils'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { BarChartModule } from '@swimlane/ngx-charts'; +import { QuizStatisticsFooterComponent } from '../quiz-statistics-footer/quiz-statistics-footer.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-quiz-point-statistic', templateUrl: './quiz-point-statistic.component.html', styleUrls: ['./quiz-point-statistic.component.scss', '../../../../../shared/chart/vertical-bar-chart.scss'], + imports: [FaIconComponent, TranslateDirective, BarChartModule, QuizStatisticsFooterComponent, ArtemisTranslatePipe], }) -export class QuizPointStatisticComponent extends QuizStatistics implements OnInit, OnDestroy { +export class QuizPointStatisticComponent extends AbstractQuizStatisticComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private router = inject(Router); + private accountService = inject(AccountService); + private quizExerciseService = inject(QuizExerciseService); + private jhiWebsocketService = inject(JhiWebsocketService); + private changeDetector = inject(ChangeDetectorRef); + private serverDateService = inject(ArtemisServerDateService); + readonly round = round; quizExercise: QuizExercise; quizPointStatistic: QuizPointStatistic; - private sub: Subscription; labels: string[] = []; @@ -48,7 +58,6 @@ export class QuizPointStatisticComponent extends QuizStatistics implements OnIni roundEdges = true; showDataLabel = true; height = 500; - tooltipDisabled = true; animations = false; // timer @@ -60,25 +69,11 @@ export class QuizPointStatisticComponent extends QuizStatistics implements OnIni // Icons faSync = faSync; - constructor( - private route: ActivatedRoute, - private router: Router, - private accountService: AccountService, - protected translateService: TranslateService, - private quizExerciseService: QuizExerciseService, - private quizStatisticUtil: QuizStatisticUtil, - private jhiWebsocketService: JhiWebsocketService, - protected changeDetector: ChangeDetectorRef, - private serverDateService: ArtemisServerDateService, - ) { - super(translateService); + ngOnInit() { this.translateService.onLangChange.subscribe(() => { this.setAxisLabels('showStatistic.quizPointStatistic.xAxes', 'showStatistic.quizPointStatistic.yAxes'); }); - } - - ngOnInit() { - this.sub = this.route.params.subscribe((params) => { + this.route.params.subscribe((params) => { // use different REST-call if the User is a Student if (this.accountService.hasAnyAuthorityDirect([Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA])) { this.quizExerciseService.find(params['exerciseId']).subscribe((res) => { @@ -142,8 +137,8 @@ export class QuizPointStatisticComponent extends QuizStatistics implements OnIni /** * Express the given timespan as humanized text * - * @param remainingTimeSeconds {number} the amount of seconds to display - * @return {string} humanized text for the given amount of seconds + * @param remainingTimeSeconds the amount of seconds to display + * @return humanized text for the given amount of seconds */ relativeTimeText(remainingTimeSeconds: number) { if (remainingTimeSeconds > 210) { @@ -163,8 +158,7 @@ export class QuizPointStatisticComponent extends QuizStatistics implements OnIni /** * load the new quizPointStatistic from the server if the Websocket has been notified * - * @param {QuizPointStatistic} statistic: the new quizPointStatistic - * from the server with the new Data. + * @param statistic the new quizPointStatistic from the server with the new Data. */ loadNewData(statistic: QuizPointStatistic) { // if the Student finds a way to the Website @@ -179,8 +173,7 @@ export class QuizPointStatisticComponent extends QuizStatistics implements OnIni /** * This functions loads the Quiz, which is necessary to build the Web-Template * - * @param {QuizExercise} quizExercise: the quizExercise, - * which this quiz-point-statistic presents. + * @param quizExercise the quizExercise, which this quiz-point-statistic presents. */ loadQuizSuccess(quizExercise: QuizExercise) { // if the Student finds a way to the Website diff --git a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic.module.ts b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic.module.ts index e11918e73541..41dac88556de 100644 --- a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic.module.ts +++ b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic.module.ts @@ -15,8 +15,12 @@ import { BarChartModule } from '@swimlane/ngx-charts'; const ENTITY_STATES = [...quizStatisticRoute]; @NgModule({ - imports: [ArtemisSharedModule, RouterModule.forChild(ENTITY_STATES), ArtemisQuizQuestionTypesModule, ArtemisMarkdownModule, BarChartModule], - declarations: [ + imports: [ + ArtemisSharedModule, + RouterModule.forChild(ENTITY_STATES), + ArtemisQuizQuestionTypesModule, + ArtemisMarkdownModule, + BarChartModule, QuizStatisticComponent, QuizPointStatisticComponent, MultipleChoiceQuestionStatisticComponent, diff --git a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic.route.ts b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic.route.ts index 15b6ae44fbca..b25b085b3da4 100644 --- a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic.route.ts +++ b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic.route.ts @@ -1,16 +1,12 @@ import { Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; -import { QuizStatisticComponent } from './quiz-statistic/quiz-statistic.component'; -import { QuizPointStatisticComponent } from './quiz-point-statistic/quiz-point-statistic.component'; -import { MultipleChoiceQuestionStatisticComponent } from './multiple-choice-question-statistic/multiple-choice-question-statistic.component'; -import { DragAndDropQuestionStatisticComponent } from './drag-and-drop-question-statistic/drag-and-drop-question-statistic.component'; -import { ShortAnswerQuestionStatisticComponent } from './short-answer-question-statistic/short-answer-question-statistic.component'; + import { Authority } from 'app/shared/constants/authority.constants'; export const quizStatisticRoute: Routes = [ { path: ':courseId/quiz-exercises/:exerciseId/quiz-statistic', - component: QuizStatisticComponent, + loadComponent: () => import('./quiz-statistic/quiz-statistic.component').then((m) => m.QuizStatisticComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.course.home.title', @@ -19,7 +15,7 @@ export const quizStatisticRoute: Routes = [ }, { path: ':courseId/quiz-exercises/:exerciseId/quiz-point-statistic', - component: QuizPointStatisticComponent, + loadComponent: () => import('./quiz-point-statistic/quiz-point-statistic.component').then((m) => m.QuizPointStatisticComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.course.home.title', @@ -28,7 +24,7 @@ export const quizStatisticRoute: Routes = [ }, { path: ':courseId/quiz-exercises/:exerciseId/mc-question-statistic/:questionId', - component: MultipleChoiceQuestionStatisticComponent, + loadComponent: () => import('./multiple-choice-question-statistic/multiple-choice-question-statistic.component').then((m) => m.MultipleChoiceQuestionStatisticComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.course.home.title', @@ -37,7 +33,7 @@ export const quizStatisticRoute: Routes = [ }, { path: ':courseId/quiz-exercises/:exerciseId/dnd-question-statistic/:questionId', - component: DragAndDropQuestionStatisticComponent, + loadComponent: () => import('./drag-and-drop-question-statistic/drag-and-drop-question-statistic.component').then((m) => m.DragAndDropQuestionStatisticComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.course.home.title', @@ -46,7 +42,7 @@ export const quizStatisticRoute: Routes = [ }, { path: ':courseId/quiz-exercises/:exerciseId/sa-question-statistic/:questionId', - component: ShortAnswerQuestionStatisticComponent, + loadComponent: () => import('./short-answer-question-statistic/short-answer-question-statistic.component').then((m) => m.ShortAnswerQuestionStatisticComponent), data: { authorities: [Authority.USER], pageTitle: 'artemisApp.course.home.title', diff --git a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic/quiz-statistic.component.ts b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic/quiz-statistic.component.ts index 9cea337abc1e..45b010a9e8df 100644 --- a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic/quiz-statistic.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistic/quiz-statistic.component.ts @@ -1,27 +1,35 @@ -import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; import { HttpResponse } from '@angular/common/http'; -import { Subscription } from 'rxjs'; -import { QuizStatisticUtil } from 'app/exercises/quiz/shared/quiz-statistic-util.service'; import { AccountService } from 'app/core/auth/account.service'; import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.service'; import { Authority } from 'app/shared/constants/authority.constants'; -import { QuizStatistics } from 'app/exercises/quiz/manage/statistics/quiz-statistics'; +import { AbstractQuizStatisticComponent } from 'app/exercises/quiz/manage/statistics/quiz-statistics'; import { faSync } from '@fortawesome/free-solid-svg-icons'; import { calculateMaxScore } from 'app/exercises/quiz/manage/statistics/quiz-statistic/quiz-statistics.utils'; import { round } from 'app/shared/util/utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { BarChartModule } from '@swimlane/ngx-charts'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { QuizStatisticsFooterComponent } from '../quiz-statistics-footer/quiz-statistics-footer.component'; @Component({ selector: 'jhi-quiz-statistic', templateUrl: './quiz-statistic.component.html', styleUrls: ['../quiz-point-statistic/quiz-point-statistic.component.scss', '../../../../../shared/chart/vertical-bar-chart.scss'], + imports: [TranslateDirective, BarChartModule, FaIconComponent, QuizStatisticsFooterComponent], }) -export class QuizStatisticComponent extends QuizStatistics implements OnInit, OnDestroy { +export class QuizStatisticComponent extends AbstractQuizStatisticComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private router = inject(Router); + private accountService = inject(AccountService); + private quizExerciseService = inject(QuizExerciseService); + private jhiWebsocketService = inject(JhiWebsocketService); + private changeDetector = inject(ChangeDetectorRef); + quizExercise: QuizExercise; - private sub: Subscription; label: string[] = []; backgroundColor: string[] = []; @@ -34,26 +42,13 @@ export class QuizStatisticComponent extends QuizStatistics implements OnInit, On // Icons faSync = faSync; - constructor( - private route: ActivatedRoute, - private router: Router, - private accountService: AccountService, - protected translateService: TranslateService, - private quizExerciseService: QuizExerciseService, - private quizStatisticUtil: QuizStatisticUtil, - private jhiWebsocketService: JhiWebsocketService, - private changeDetector: ChangeDetectorRef, - ) { - super(translateService); + ngOnInit() { this.translateService.onLangChange.subscribe(() => { this.setAxisLabels('showStatistic.quizStatistic.xAxes', 'showStatistic.quizStatistic.yAxes'); this.ngxData[this.ngxData.length - 1].name = this.translateService.instant('showStatistic.quizStatistic.average'); this.ngxData = [...this.ngxData]; }); - } - - ngOnInit() { - this.sub = this.route.params.subscribe((params) => { + this.route.params.subscribe((params) => { // use different REST-call if the User is a Student if (this.accountService.hasAnyAuthorityDirect([Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA])) { this.quizExerciseService.find(params['exerciseId']).subscribe((res: HttpResponse) => { @@ -85,7 +80,7 @@ export class QuizStatisticComponent extends QuizStatistics implements OnInit, On * This functions loads the Quiz, which is necessary to build the Web-Template * And it loads the new Data if the Websocket has been notified * - * @param {QuizExercise} quiz: the quizExercise, which this quiz-statistic presents. + * @param quiz the quizExercise, which this quiz-statistic presents. */ loadQuizSuccess(quiz: QuizExercise) { // if the Student finds a way to the Website -> the Student will be sent back to Courses diff --git a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistics-footer/quiz-statistics-footer.component.ts b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistics-footer/quiz-statistics-footer.component.ts index 8765ef7488a4..f3cc4662659c 100644 --- a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistics-footer/quiz-statistics-footer.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistics-footer/quiz-statistics-footer.component.ts @@ -1,5 +1,5 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; +import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { QuizStatisticUtil } from 'app/exercises/quiz/shared/quiz-statistic-util.service'; import { ShortAnswerQuestionUtil } from 'app/exercises/quiz/shared/short-answer-question-util.service'; import { TranslateService } from '@ngx-translate/core'; @@ -16,14 +16,30 @@ import { Authority } from 'app/shared/constants/authority.constants'; import { UI_RELOAD_TIME } from 'app/shared/constants/exercise-exam-constants'; import { faListAlt } from '@fortawesome/free-regular-svg-icons'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; +import { NgbDropdown, NgbDropdownMenu, NgbDropdownToggle } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgClass } from '@angular/common'; +import { JhiConnectionStatusComponent } from 'app/shared/connection-status/connection-status.component'; +import { TruncatePipe } from 'app/shared/pipes/truncate.pipe'; @Component({ selector: 'jhi-quiz-statistics-footer', templateUrl: './quiz-statistics-footer.component.html', providers: [QuizStatisticUtil, ShortAnswerQuestionUtil], styleUrls: ['./quiz-statistics-footer.component.scss', '../../../shared/quiz.scss'], + imports: [NgbDropdown, NgbDropdownToggle, FaIconComponent, TranslateDirective, NgbDropdownMenu, RouterLink, NgClass, JhiConnectionStatusComponent, TruncatePipe], }) export class QuizStatisticsFooterComponent implements OnInit, OnDestroy { + private route = inject(ActivatedRoute); + private router = inject(Router); + private accountService = inject(AccountService); + private translateService = inject(TranslateService); + private quizExerciseService = inject(QuizExerciseService); + private quizStatisticUtil = inject(QuizStatisticUtil); + private jhiWebsocketService = inject(JhiWebsocketService); + private serverDateService = inject(ArtemisServerDateService); + @Input() isQuizPointStatistic: boolean; @Input() isQuizStatistic: boolean; @@ -47,17 +63,6 @@ export class QuizStatisticsFooterComponent implements OnInit, OnDestroy { // Icons farListAlt = faListAlt; - constructor( - private route: ActivatedRoute, - private router: Router, - private accountService: AccountService, - private translateService: TranslateService, - private quizExerciseService: QuizExerciseService, - private quizStatisticUtil: QuizStatisticUtil, - private jhiWebsocketService: JhiWebsocketService, - private serverDateService: ArtemisServerDateService, - ) {} - ngOnInit() { this.sub = this.route.params.subscribe((params) => { this.questionIdParam = +params['questionId']; diff --git a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistics.ts b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistics.ts index 39feb170c584..6598c34e86b8 100644 --- a/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistics.ts +++ b/src/main/webapp/app/exercises/quiz/manage/statistics/quiz-statistics.ts @@ -1,11 +1,15 @@ -import { ChangeDetectorRef } from '@angular/core'; +import { ChangeDetectorRef, Component, inject } from '@angular/core'; import { Color, ScaleType } from '@swimlane/ngx-charts'; import { round } from 'app/shared/util/utils'; import { QuizStatistic } from 'app/entities/quiz/quiz-statistic.model'; import { TranslateService } from '@ngx-translate/core'; import { NgxChartsSingleSeriesDataEntry } from 'app/shared/chart/ngx-charts-datatypes'; -export abstract class QuizStatistics { +@Component({ + template: '', +}) +export abstract class AbstractQuizStatisticComponent { + protected translateService = inject(TranslateService); data: number[] = []; ratedData: number[] = []; unratedData: number[] = []; @@ -26,8 +30,6 @@ export abstract class QuizStatistics { chartLabels: string[] = []; totalParticipants = 0; - constructor(protected translateService: TranslateService) {} - /** * Depending on if the rated or unrated results should be displayed, * The amount of participants as well as the corresponding scores are set diff --git a/src/main/webapp/app/exercises/quiz/manage/statistics/short-answer-question-statistic/short-answer-question-statistic.component.ts b/src/main/webapp/app/exercises/quiz/manage/statistics/short-answer-question-statistic/short-answer-question-statistic.component.ts index 1f60773b9d81..401a339ed563 100644 --- a/src/main/webapp/app/exercises/quiz/manage/statistics/short-answer-question-statistic/short-answer-question-statistic.component.ts +++ b/src/main/webapp/app/exercises/quiz/manage/statistics/short-answer-question-statistic/short-answer-question-statistic.component.ts @@ -1,18 +1,17 @@ -import { ChangeDetectorRef, Component } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { TranslateService } from '@ngx-translate/core'; +import { Component, inject } from '@angular/core'; import { QuizStatisticUtil } from 'app/exercises/quiz/shared/quiz-statistic-util.service'; import { ShortAnswerQuestionUtil } from 'app/exercises/quiz/shared/short-answer-question-util.service'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; -import { AccountService } from 'app/core/auth/account.service'; -import { JhiWebsocketService } from 'app/core/websocket/websocket.service'; import { ShortAnswerQuestion } from 'app/entities/quiz/short-answer-question.model'; -import { QuizExerciseService } from 'app/exercises/quiz/manage/quiz-exercise.service'; import { ShortAnswerQuestionStatistic } from 'app/entities/quiz/short-answer-question-statistic.model'; import { ShortAnswerSolution } from 'app/entities/quiz/short-answer-solution.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; import { QuestionStatisticComponent, blueColor, greenColor } from 'app/exercises/quiz/manage/statistics/question-statistic.component'; import { faCheckCircle, faSync, faTimesCircle } from '@fortawesome/free-solid-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { BarChartModule } from '@swimlane/ngx-charts'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { QuizStatisticsFooterComponent } from '../quiz-statistics-footer/quiz-statistics-footer.component'; @Component({ selector: 'jhi-short-answer-question-statistic', @@ -23,8 +22,12 @@ import { faCheckCircle, faSync, faTimesCircle } from '@fortawesome/free-solid-sv '../quiz-point-statistic/quiz-point-statistic.component.scss', './short-answer-question-statistic.component.scss', ], + imports: [TranslateDirective, BarChartModule, FaIconComponent, QuizStatisticsFooterComponent], }) export class ShortAnswerQuestionStatisticComponent extends QuestionStatisticComponent { + shortAnswerQuestionUtil = inject(ShortAnswerQuestionUtil); + private artemisMarkdown = inject(ArtemisMarkdownService); + declare question: ShortAnswerQuestion; textParts: string[][]; @@ -37,27 +40,6 @@ export class ShortAnswerQuestionStatisticComponent extends QuestionStatisticComp faCheckCircle = faCheckCircle; faTimesCircle = faTimesCircle; - constructor( - route: ActivatedRoute, - router: Router, - accountService: AccountService, - translateService: TranslateService, - quizExerciseService: QuizExerciseService, - jhiWebsocketService: JhiWebsocketService, - quizStatisticUtil: QuizStatisticUtil, - public shortAnswerQuestionUtil: ShortAnswerQuestionUtil, - private artemisMarkdown: ArtemisMarkdownService, - protected changeDetector: ChangeDetectorRef, - ) { - super(route, router, accountService, translateService, quizExerciseService, jhiWebsocketService, changeDetector); - } - - /** - * This functions loads the Quiz, which is necessary to build the Web-Template - * - * @param {QuizExercise} quiz: the quizExercise, which the selected question is part of. - * @param {boolean} refresh: true if method is called from Websocket - */ loadQuiz(quiz: QuizExercise, refresh: boolean) { const updatedQuestion = super.loadQuizCommon(quiz); if (!updatedQuestion) { @@ -102,8 +84,8 @@ export class ShortAnswerQuestionStatisticComponent extends QuestionStatisticComp loadLayout() { this.resetLabelsColors(); - // set label and backgroundcolor based on the spots - this.question.spots!.forEach((spot, i) => { + // set label and background color based on the spots + this.question.spots!.forEach((_spot, i) => { this.labels.push(this.getLetter(i) + '.'); this.solutionLabels.push(this.getLetter(i) + '.'); this.backgroundColors.push(blueColor); diff --git a/src/main/webapp/app/exercises/quiz/participate/quiz-participation.component.ts b/src/main/webapp/app/exercises/quiz/participate/quiz-participation.component.ts index 599e48e43a22..70a3f28af980 100644 --- a/src/main/webapp/app/exercises/quiz/participate/quiz-participation.component.ts +++ b/src/main/webapp/app/exercises/quiz/participate/quiz-participation.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit, QueryList, ViewChildren } from '@angular/core'; +import { Component, OnDestroy, OnInit, QueryList, ViewChildren, inject } from '@angular/core'; import dayjs from 'dayjs/esm'; import isMobile from 'ismobilejs-es5'; import { HttpErrorResponse, HttpResponse } from '@angular/common/http'; @@ -38,14 +38,49 @@ import { captureException } from '@sentry/angular'; import { getCourseFromExercise } from 'app/entities/exercise.model'; import { faCircleNotch, faSync } from '@fortawesome/free-solid-svg-icons'; import { ArtemisServerDateService } from 'app/shared/server-date.service'; +import { NgClass, NgTemplateOutlet } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { ButtonComponent } from 'app/shared/components/button.component'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { JhiConnectionStatusComponent } from 'app/shared/connection-status/connection-status.component'; +import { FormsModule } from '@angular/forms'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-quiz', templateUrl: './quiz-participation.component.html', providers: [ParticipationService], styleUrls: ['./quiz-participation.component.scss'], + imports: [ + NgClass, + NgTemplateOutlet, + TranslateDirective, + ButtonComponent, + NgbTooltip, + MultipleChoiceQuestionComponent, + DragAndDropQuestionComponent, + ShortAnswerQuestionComponent, + JhiConnectionStatusComponent, + FormsModule, + FaIconComponent, + ArtemisDatePipe, + ArtemisTranslatePipe, + ], }) export class QuizParticipationComponent implements OnInit, OnDestroy { + private jhiWebsocketService = inject(JhiWebsocketService); + private quizExerciseService = inject(QuizExerciseService); + private participationService = inject(ParticipationService); + private route = inject(ActivatedRoute); + private router = inject(Router); + private alertService = inject(AlertService); + private quizParticipationService = inject(QuizParticipationService); + private translateService = inject(TranslateService); + private quizService = inject(ArtemisQuizService); + private serverDateService = inject(ArtemisServerDateService); + // make constants available to html for comparison readonly DRAG_AND_DROP = QuizQuestionType.DRAG_AND_DROP; readonly MULTIPLE_CHOICE = QuizQuestionType.MULTIPLE_CHOICE; @@ -128,18 +163,7 @@ export class QuizParticipationComponent implements OnInit, OnDestroy { faSync = faSync; faCircleNotch = faCircleNotch; - constructor( - private jhiWebsocketService: JhiWebsocketService, - private quizExerciseService: QuizExerciseService, - private participationService: ParticipationService, - private route: ActivatedRoute, - private router: Router, - private alertService: AlertService, - private quizParticipationService: QuizParticipationService, - private translateService: TranslateService, - private quizService: ArtemisQuizService, - private serverDateService: ArtemisServerDateService, - ) { + constructor() { smoothscroll.polyfill(); } diff --git a/src/main/webapp/app/exercises/quiz/participate/quiz-participation.module.ts b/src/main/webapp/app/exercises/quiz/participate/quiz-participation.module.ts index 596dcbec2306..30682ecaf6d2 100644 --- a/src/main/webapp/app/exercises/quiz/participate/quiz-participation.module.ts +++ b/src/main/webapp/app/exercises/quiz/participate/quiz-participation.module.ts @@ -1,13 +1,12 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { quizParticipationRoute } from './quiz-participation.route'; -import { QuizParticipationComponent } from './quiz-participation.component'; +import { quizParticipationRoute } from 'app/exercises/quiz/participate/quiz-participation.route'; +import { QuizParticipationComponent } from 'app/exercises/quiz/participate/quiz-participation.component'; import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module'; import { ArtemisSharedModule } from 'app/shared/shared.module'; import { ArtemisQuizQuestionTypesModule } from 'app/exercises/quiz/shared/questions/artemis-quiz-question-types.module'; @NgModule({ - imports: [ArtemisSharedModule, RouterModule.forChild(quizParticipationRoute), ArtemisSharedComponentModule, ArtemisQuizQuestionTypesModule], - declarations: [QuizParticipationComponent], + imports: [ArtemisSharedModule, RouterModule.forChild(quizParticipationRoute), ArtemisSharedComponentModule, ArtemisQuizQuestionTypesModule, QuizParticipationComponent], }) export class ArtemisQuizParticipationModule {} diff --git a/src/main/webapp/app/exercises/quiz/participate/quiz-participation.route.ts b/src/main/webapp/app/exercises/quiz/participate/quiz-participation.route.ts index 4c6e37d7b94e..48d5110a64b2 100644 --- a/src/main/webapp/app/exercises/quiz/participate/quiz-participation.route.ts +++ b/src/main/webapp/app/exercises/quiz/participate/quiz-participation.route.ts @@ -1,12 +1,11 @@ import { Routes } from '@angular/router'; -import { QuizParticipationComponent } from './quiz-participation.component'; import { UserRouteAccessService } from 'app/core/auth/user-route-access-service'; export const quizParticipationRoute: Routes = [ { path: 'live', - component: QuizParticipationComponent, + loadComponent: () => import('./quiz-participation.component').then((m) => m.QuizParticipationComponent), data: { authorities: [], pageTitle: 'artemisApp.quizExercise.home.title', @@ -16,7 +15,7 @@ export const quizParticipationRoute: Routes = [ }, { path: 'practice', - component: QuizParticipationComponent, + loadComponent: () => import('./quiz-participation.component').then((m) => m.QuizParticipationComponent), data: { authorities: [], pageTitle: 'artemisApp.quizExercise.home.title', diff --git a/src/main/webapp/app/exercises/quiz/participate/quiz-participation.service.ts b/src/main/webapp/app/exercises/quiz/participate/quiz-participation.service.ts index f6eb1bdc696c..5ccce2db131b 100644 --- a/src/main/webapp/app/exercises/quiz/participate/quiz-participation.service.ts +++ b/src/main/webapp/app/exercises/quiz/participate/quiz-participation.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; import { Observable } from 'rxjs'; import { QuizSubmission } from 'app/entities/quiz/quiz-submission.model'; @@ -11,10 +11,8 @@ export type ResultResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class QuizParticipationService { - constructor( - private http: HttpClient, - private submissionService: SubmissionService, - ) {} + private http = inject(HttpClient); + private submissionService = inject(SubmissionService); submitForPractice(quizSubmission: QuizSubmission, exerciseId: number): Observable { const copy = this.submissionService.convert(quizSubmission); diff --git a/src/main/webapp/app/exercises/quiz/shared/drag-and-drop-question-util.service.ts b/src/main/webapp/app/exercises/quiz/shared/drag-and-drop-question-util.service.ts index 741254eb7446..73462d5e6752 100644 --- a/src/main/webapp/app/exercises/quiz/shared/drag-and-drop-question-util.service.ts +++ b/src/main/webapp/app/exercises/quiz/shared/drag-and-drop-question-util.service.ts @@ -6,8 +6,6 @@ import { BaseEntityWithTempId, DropLocation } from 'app/entities/quiz/drop-locat @Injectable({ providedIn: 'root' }) export class DragAndDropQuestionUtil { - constructor() {} - /** * Get a sample solution for the given drag and drop question * diff --git a/src/main/webapp/app/exercises/quiz/shared/fit-text/fit-text.directive.ts b/src/main/webapp/app/exercises/quiz/shared/fit-text/fit-text.directive.ts index eb0c9fc9ba89..b41c9d4b1924 100644 --- a/src/main/webapp/app/exercises/quiz/shared/fit-text/fit-text.directive.ts +++ b/src/main/webapp/app/exercises/quiz/shared/fit-text/fit-text.directive.ts @@ -1,10 +1,11 @@ -import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnChanges, OnInit, Renderer2, SimpleChanges } from '@angular/core'; +import { AfterViewInit, Directive, ElementRef, HostListener, Input, OnChanges, OnDestroy, OnInit, Renderer2, SimpleChanges, inject } from '@angular/core'; // NOTE: this code was taken from https://github.com/sollenne/angular-fittext because the repository was not maintained any more since June 2018 -// eslint-disable-next-line @angular-eslint/directive-selector @Directive({ selector: '[fitText]' }) -export class FitTextDirective implements AfterViewInit, OnInit, OnChanges { +export class FitTextDirective implements AfterViewInit, OnInit, OnChanges, OnDestroy { + private renderer = inject(Renderer2); + @Input() fitText = true; @Input() compression = 1; @Input() activateOnResize = true; @@ -23,12 +24,11 @@ export class FitTextDirective implements AfterViewInit, OnInit, OnChanges { private fitTextMinFontSize: number; private fitTextMaxFontSize: number; private calcSize = 10; - private resizeTimeout: any; + private resizeTimeout: NodeJS.Timeout; + + constructor() { + const el = inject(ElementRef); - constructor( - private el: ElementRef, - private renderer: Renderer2, - ) { this.fitTextElement = el.nativeElement; this.fitTextParent = this.fitTextElement.parentElement!; this.computed = window.getComputedStyle(this.fitTextElement); @@ -65,6 +65,10 @@ export class FitTextDirective implements AfterViewInit, OnInit, OnChanges { } } + public ngOnDestroy() { + clearTimeout(this.resizeTimeout); + } + private setFontSize = (delay: number = this.delay): void => { this.resizeTimeout = setTimeout(() => { if (this.fitTextElement.offsetHeight * this.fitTextElement.offsetWidth !== 0) { diff --git a/src/main/webapp/app/exercises/quiz/shared/fit-text/fit-text.module.ts b/src/main/webapp/app/exercises/quiz/shared/fit-text/fit-text.module.ts deleted file mode 100644 index f2e9ed2dccdc..000000000000 --- a/src/main/webapp/app/exercises/quiz/shared/fit-text/fit-text.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NgModule } from '@angular/core'; -import { FitTextDirective } from 'app/exercises/quiz/shared/fit-text/fit-text.directive'; - -@NgModule({ - declarations: [FitTextDirective], - exports: [FitTextDirective], -}) -export class FitTextModule {} diff --git a/src/main/webapp/app/exercises/quiz/shared/questions/artemis-quiz-question-types.module.ts b/src/main/webapp/app/exercises/quiz/shared/questions/artemis-quiz-question-types.module.ts index 65ca1d3acea7..3d1bc34d5a45 100644 --- a/src/main/webapp/app/exercises/quiz/shared/questions/artemis-quiz-question-types.module.ts +++ b/src/main/webapp/app/exercises/quiz/shared/questions/artemis-quiz-question-types.module.ts @@ -7,12 +7,14 @@ import { DragItemComponent } from './drag-and-drop-question/drag-item.component' import { DragDropModule } from '@angular/cdk/drag-drop'; import { QuizScoringInfoStudentModalComponent } from 'app/exercises/quiz/shared/questions/quiz-scoring-infostudent-modal/quiz-scoring-info-student-modal.component'; import { ArtemisMarkdownModule } from 'app/shared/markdown.module'; -import { FitTextModule } from 'app/exercises/quiz/shared/fit-text/fit-text.module'; + import { MultipleChoiceVisualQuestionComponent } from 'app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-visual-question.component'; @NgModule({ - imports: [ArtemisSharedModule, DragDropModule, ArtemisMarkdownModule, FitTextModule], - declarations: [ + imports: [ + ArtemisSharedModule, + DragDropModule, + ArtemisMarkdownModule, DragItemComponent, DragAndDropQuestionComponent, MultipleChoiceQuestionComponent, diff --git a/src/main/webapp/app/exercises/quiz/shared/questions/drag-and-drop-question/drag-and-drop-question.component.ts b/src/main/webapp/app/exercises/quiz/shared/questions/drag-and-drop-question/drag-and-drop-question.component.ts index 64275c7e790f..b935449e83f0 100644 --- a/src/main/webapp/app/exercises/quiz/shared/questions/drag-and-drop-question/drag-and-drop-question.component.ts +++ b/src/main/webapp/app/exercises/quiz/shared/questions/drag-and-drop-question/drag-and-drop-question.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild, ViewEncapsulation, inject } from '@angular/core'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { DragAndDropQuestionUtil } from 'app/exercises/quiz/shared/drag-and-drop-question-util.service'; import { polyfill } from 'mobile-drag-drop'; @@ -9,8 +9,14 @@ import { DragAndDropMapping } from 'app/entities/quiz/drag-and-drop-mapping.mode import { RenderedQuizQuestionMarkDownElement } from 'app/entities/quiz/quiz-question.model'; import { DropLocation } from 'app/entities/quiz/drop-location.model'; import { faExclamationCircle, faExclamationTriangle, faQuestionCircle, faSpinner } from '@fortawesome/free-solid-svg-icons'; -import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { CdkDragDrop, CdkDropList, CdkDropListGroup } from '@angular/cdk/drag-drop'; import { DragItem } from 'app/entities/quiz/drag-item.model'; +import { NgClass, NgStyle } from '@angular/common'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbPopover, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { QuizScoringInfoStudentModalComponent } from '../quiz-scoring-infostudent-modal/quiz-scoring-info-student-modal.component'; +import { DragItemComponent } from './drag-item.component'; // options are optional ;) polyfill({ @@ -36,8 +42,24 @@ enum MappingResult { providers: [DragAndDropQuestionUtil], styleUrls: ['./drag-and-drop-question.component.scss', '../../../participate/quiz-participation.scss'], encapsulation: ViewEncapsulation.None, + imports: [ + NgClass, + FaIconComponent, + TranslateDirective, + NgbPopover, + QuizScoringInfoStudentModalComponent, + CdkDropListGroup, + SecuredImageComponent, + CdkDropList, + NgStyle, + DragItemComponent, + NgbTooltip, + ], }) export class DragAndDropQuestionComponent implements OnChanges, OnInit { + private artemisMarkdown = inject(ArtemisMarkdownService); + private dragAndDropQuestionUtil = inject(DragAndDropQuestionUtil); + /** needed to trigger a manual reload of the drag and drop background picture */ @ViewChild(SecuredImageComponent, { static: false }) secureImageComponent: SecuredImageComponent; @@ -100,16 +122,11 @@ export class DragAndDropQuestionComponent implements OnChanges, OnInit { faExclamationTriangle = faExclamationTriangle; faExclamationCircle = faExclamationCircle; - constructor( - private artemisMarkdown: ArtemisMarkdownService, - private dragAndDropQuestionUtil: DragAndDropQuestionUtil, - ) {} - ngOnInit(): void { this.evaluateDropLocations(); } - ngOnChanges(): void { + ngOnChanges() { this.evaluateDropLocations(); } @@ -215,8 +232,8 @@ export class DragAndDropQuestionComponent implements OnChanges, OnInit { /** * Get the drag item that was mapped to the given drop location * - * @param dropLocation {object} the drop location that the drag item should be mapped to - * @return {object | undefined} the mapped drag item, or undefined, if no drag item has been mapped to this location + * @param dropLocation the drop location that the drag item should be mapped to + * @return the mapped drag item, or undefined, if no drag item has been mapped to this location */ dragItemForDropLocation(dropLocation: DropLocation) { if (this.mappings) { @@ -238,7 +255,7 @@ export class DragAndDropQuestionComponent implements OnChanges, OnInit { /** * Get all drag items that have not been assigned to a drop location yet * - * @return {Array} an array of all unassigned drag items + * @returnan array of all unassigned drag items */ getUnassignedDragItems() { return this.question.dragItems?.filter((dragItem) => { @@ -312,8 +329,8 @@ export class DragAndDropQuestionComponent implements OnChanges, OnInit { /** * Get the drag item that was mapped to the given drop location in the sample solution * - * @param dropLocation {object} the drop location that the drag item should be mapped to - * @return {DragItem | undefined} the mapped drag item, or undefined, if no drag item has been mapped to this location + * @param dropLocation the drop location that the drag item should be mapped to + * @return the mapped drag item, or undefined, if no drag item has been mapped to this location */ correctDragItemForDropLocation(dropLocation: DropLocation) { const dragAndDropQuestionUtil = this.dragAndDropQuestionUtil; diff --git a/src/main/webapp/app/exercises/quiz/shared/questions/drag-and-drop-question/drag-item.component.ts b/src/main/webapp/app/exercises/quiz/shared/questions/drag-and-drop-question/drag-item.component.ts index 1d2b06714b93..3d4e920820d5 100644 --- a/src/main/webapp/app/exercises/quiz/shared/questions/drag-and-drop-question/drag-item.component.ts +++ b/src/main/webapp/app/exercises/quiz/shared/questions/drag-and-drop-question/drag-item.component.ts @@ -1,12 +1,18 @@ import { Component, Input, OnInit, ViewEncapsulation } from '@angular/core'; import isMobile from 'ismobilejs-es5'; import { DragItem } from 'app/entities/quiz/drag-item.model'; +import { NgClass, NgStyle } from '@angular/common'; +import { CdkDrag, CdkDragPlaceholder, CdkDragPreview } from '@angular/cdk/drag-drop'; +import { SecuredImageComponent } from 'app/shared/image/secured-image.component'; +import { FitTextDirective } from '../../fit-text/fit-text.directive'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-drag-item', templateUrl: './drag-item.component.html', styleUrls: ['./drag-item.component.scss'], encapsulation: ViewEncapsulation.None, + imports: [NgClass, NgStyle, CdkDrag, SecuredImageComponent, CdkDragPlaceholder, FitTextDirective, CdkDragPreview, TranslateDirective], }) export class DragItemComponent implements OnInit { @Input() minWidth: string; @@ -16,8 +22,6 @@ export class DragItemComponent implements OnInit { @Input() filePreviewPaths: Map = new Map(); isMobile = false; - constructor() {} - /** * Initializes device information and whether the device is a mobile device */ diff --git a/src/main/webapp/app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-question.component.ts b/src/main/webapp/app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-question.component.ts index 183d2f78a897..26a84e2563d9 100644 --- a/src/main/webapp/app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-question.component.ts +++ b/src/main/webapp/app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-question.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ViewEncapsulation, inject } from '@angular/core'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { AnswerOption } from 'app/entities/quiz/answer-option.model'; import { MultipleChoiceQuestion } from 'app/entities/quiz/multiple-choice-question.model'; @@ -6,14 +6,23 @@ import { QuizQuestion, RenderedQuizQuestionMarkDownElement } from 'app/entities/ import { Result } from 'app/entities/result.model'; import { faExclamationCircle, faExclamationTriangle, faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { faCheckSquare, faCircle, faDotCircle, faSquare } from '@fortawesome/free-regular-svg-icons'; +import { NgClass } from '@angular/common'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { NgbPopover, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { QuizScoringInfoStudentModalComponent } from '../quiz-scoring-infostudent-modal/quiz-scoring-info-student-modal.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-multiple-choice-question', templateUrl: './multiple-choice-question.component.html', styleUrls: ['./multiple-choice-question.component.scss', '../../../participate/quiz-participation.scss'], encapsulation: ViewEncapsulation.None, + imports: [NgClass, TranslateDirective, NgbPopover, FaIconComponent, QuizScoringInfoStudentModalComponent, NgbTooltip, ArtemisTranslatePipe], }) export class MultipleChoiceQuestionComponent { + private artemisMarkdown = inject(ArtemisMarkdownService); + _question: MultipleChoiceQuestion; @Input() @@ -58,8 +67,6 @@ export class MultipleChoiceQuestionComponent { faCircle = faCircle; faDotCircle = faDotCircle; - constructor(private artemisMarkdown: ArtemisMarkdownService) {} - /** * Update html for text, hint and explanation for the question and every answer option */ diff --git a/src/main/webapp/app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-visual-question.component.ts b/src/main/webapp/app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-visual-question.component.ts index 5d3ce99651c3..2f8d68fe691e 100644 --- a/src/main/webapp/app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-visual-question.component.ts +++ b/src/main/webapp/app/exercises/quiz/shared/questions/multiple-choice-question/multiple-choice-visual-question.component.ts @@ -3,12 +3,19 @@ import { MultipleChoiceQuestion } from 'app/entities/quiz/multiple-choice-questi import { faCheck, faExclamationCircle, faExclamationTriangle, faPlus, faQuestionCircle, faTrash, faXmark } from '@fortawesome/free-solid-svg-icons'; import { faCircle } from '@fortawesome/free-regular-svg-icons'; import { AnswerOption } from 'app/entities/quiz/answer-option.model'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FormsModule } from '@angular/forms'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { NgClass } from '@angular/common'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-multiple-choice-visual-question', templateUrl: './multiple-choice-visual-question.component.html', styleUrls: ['./multiple-choice-question.component.scss', '../../../participate/quiz-participation.scss'], encapsulation: ViewEncapsulation.None, + imports: [TranslateDirective, FormsModule, FaIconComponent, NgbTooltip, NgClass, ArtemisTranslatePipe], }) export class MultipleChoiceVisualQuestionComponent { _question: MultipleChoiceQuestion; @@ -33,8 +40,6 @@ export class MultipleChoiceVisualQuestionComponent { faTrash = faTrash; faXmark = faXmark; - constructor() {} - parseQuestion() { let markdown = this.question.text ?? ''; diff --git a/src/main/webapp/app/exercises/quiz/shared/questions/quiz-scoring-infostudent-modal/quiz-scoring-info-student-modal.component.ts b/src/main/webapp/app/exercises/quiz/shared/questions/quiz-scoring-infostudent-modal/quiz-scoring-info-student-modal.component.ts index 5ac7d0af0c23..864d808ca87a 100644 --- a/src/main/webapp/app/exercises/quiz/shared/questions/quiz-scoring-infostudent-modal/quiz-scoring-info-student-modal.component.ts +++ b/src/main/webapp/app/exercises/quiz/shared/questions/quiz-scoring-infostudent-modal/quiz-scoring-info-student-modal.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, Input } from '@angular/core'; +import { AfterViewInit, Component, Input, inject } from '@angular/core'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { Result } from 'app/entities/result.model'; @@ -11,13 +11,20 @@ import { AnswerOption } from 'app/entities/quiz/answer-option.model'; import { ShortAnswerSubmittedText } from 'app/entities/quiz/short-answer-submitted-text.model'; import { MultipleChoiceQuestion } from 'app/entities/quiz/multiple-choice-question.model'; import { faQuestionCircle } from '@fortawesome/free-regular-svg-icons'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { NgClass } from '@angular/common'; @Component({ selector: 'jhi-quiz-scoring-infostudent-modal', templateUrl: './quiz-scoring-info-student-modal.component.html', styleUrls: ['./quiz-scoring-info-student-modal.component.scss'], + imports: [TranslateDirective, FaIconComponent, NgClass], }) export class QuizScoringInfoStudentModalComponent implements AfterViewInit { + private modalService = inject(NgbModal); + private translateService = inject(TranslateService); + QuizQuestionType = QuizQuestionType; ScoringType = ScoringType; @@ -68,11 +75,6 @@ export class QuizScoringInfoStudentModalComponent implements AfterViewInit { // Icons farQuestionCircle = faQuestionCircle; - constructor( - private modalService: NgbModal, - private translateService: TranslateService, - ) {} - /** * Count the variables depending on the quiz question type */ diff --git a/src/main/webapp/app/exercises/quiz/shared/questions/short-answer-question/short-answer-question.component.ts b/src/main/webapp/app/exercises/quiz/shared/questions/short-answer-question/short-answer-question.component.ts index 8d6e7a41e96e..523ac041f6a5 100644 --- a/src/main/webapp/app/exercises/quiz/shared/questions/short-answer-question/short-answer-question.component.ts +++ b/src/main/webapp/app/exercises/quiz/shared/questions/short-answer-question/short-answer-question.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core'; +import { Component, EventEmitter, Input, Output, ViewEncapsulation, inject } from '@angular/core'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { ShortAnswerQuestionUtil } from 'app/exercises/quiz/shared/short-answer-question-util.service'; import { ShortAnswerSolution } from 'app/entities/quiz/short-answer-solution.model'; @@ -8,6 +8,12 @@ import { QuizQuestion, RenderedQuizQuestionMarkDownElement } from 'app/entities/ import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; import { faQuestionCircle } from '@fortawesome/free-regular-svg-icons'; import { MAX_QUIZ_SHORT_ANSWER_TEXT_LENGTH } from 'app/shared/constants/input.constants'; +import { NgClass } from '@angular/common'; +import { NgbPopover, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { QuizScoringInfoStudentModalComponent } from '../quiz-scoring-infostudent-modal/quiz-scoring-info-student-modal.component'; +import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; @Component({ selector: 'jhi-short-answer-question', @@ -15,8 +21,12 @@ import { MAX_QUIZ_SHORT_ANSWER_TEXT_LENGTH } from 'app/shared/constants/input.co providers: [ShortAnswerQuestionUtil], styleUrls: ['./short-answer-question.component.scss', '../../../participate/quiz-participation.scss'], encapsulation: ViewEncapsulation.None, + imports: [NgClass, NgbTooltip, TranslateDirective, NgbPopover, FaIconComponent, QuizScoringInfoStudentModalComponent, ArtemisTranslatePipe], }) export class ShortAnswerQuestionComponent { + private artemisMarkdown = inject(ArtemisMarkdownService); + shortAnswerQuestionUtil = inject(ShortAnswerQuestionUtil); + shortAnswerQuestion: ShortAnswerQuestion; _forceSampleSolution: boolean; @@ -65,11 +75,6 @@ export class ShortAnswerQuestionComponent { faExclamationCircle = faExclamationCircle; farQuestionCircle = faQuestionCircle; - constructor( - private artemisMarkdown: ArtemisMarkdownService, - public shortAnswerQuestionUtil: ShortAnswerQuestionUtil, - ) {} - /** * Update html for text, hint and explanation for the question and every answer option */ diff --git a/src/main/webapp/app/exercises/quiz/shared/quiz-statistic-util.service.ts b/src/main/webapp/app/exercises/quiz/shared/quiz-statistic-util.service.ts index dc4464ed8fed..2a2f80ad30b6 100644 --- a/src/main/webapp/app/exercises/quiz/shared/quiz-statistic-util.service.ts +++ b/src/main/webapp/app/exercises/quiz/shared/quiz-statistic-util.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Router } from '@angular/router'; import { QuizQuestion, QuizQuestionType } from 'app/entities/quiz/quiz-question.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; @@ -6,7 +6,7 @@ import { getCourseId } from 'app/entities/exercise.model'; @Injectable({ providedIn: 'root' }) export class QuizStatisticUtil { - constructor(private router: Router) {} + private router = inject(Router); /** * Gets the URL to the quiz exercise detail/edit view diff --git a/src/main/webapp/app/exercises/quiz/shared/short-answer-question-util.service.ts b/src/main/webapp/app/exercises/quiz/shared/short-answer-question-util.service.ts index 6e6aac7e24eb..232ba77bc1dc 100644 --- a/src/main/webapp/app/exercises/quiz/shared/short-answer-question-util.service.ts +++ b/src/main/webapp/app/exercises/quiz/shared/short-answer-question-util.service.ts @@ -8,8 +8,6 @@ import { htmlForMarkdown } from 'app/shared/util/markdown.conversion.util'; @Injectable({ providedIn: 'root' }) export class ShortAnswerQuestionUtil { - constructor() {} - /** * Validate that no mapping exists that makes it impossible to solve the question. * We iterate through all spots and remove all possible mappings (solutions) for that spot. diff --git a/src/main/webapp/app/exercises/shared/assessment-progress-label/assessment-progress-label.component.ts b/src/main/webapp/app/exercises/shared/assessment-progress-label/assessment-progress-label.component.ts index 254a3b2a5f82..3b6942134bdb 100644 --- a/src/main/webapp/app/exercises/shared/assessment-progress-label/assessment-progress-label.component.ts +++ b/src/main/webapp/app/exercises/shared/assessment-progress-label/assessment-progress-label.component.ts @@ -1,10 +1,13 @@ import { Component, Input, OnChanges } from '@angular/core'; import { Submission, getLatestSubmissionResult } from 'app/entities/submission.model'; import { isManualResult } from 'app/exercises/shared/result/result.utils'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { DecimalPipe, NgClass } from '@angular/common'; @Component({ selector: 'jhi-assessment-progress-label', templateUrl: './assessment-progress-label.component.html', + imports: [TranslateDirective, NgClass, DecimalPipe], }) export class AssessmentProgressLabelComponent implements OnChanges { @Input() diff --git a/src/main/webapp/app/exercises/shared/assessment-progress-label/assessment-progress-label.module.ts b/src/main/webapp/app/exercises/shared/assessment-progress-label/assessment-progress-label.module.ts index 9b8bfa4925f0..8516a51d2e75 100644 --- a/src/main/webapp/app/exercises/shared/assessment-progress-label/assessment-progress-label.module.ts +++ b/src/main/webapp/app/exercises/shared/assessment-progress-label/assessment-progress-label.module.ts @@ -3,8 +3,7 @@ import { ArtemisSharedModule } from 'app/shared/shared.module'; import { AssessmentProgressLabelComponent } from 'app/exercises/shared/assessment-progress-label/assessment-progress-label.component'; @NgModule({ - imports: [ArtemisSharedModule], - declarations: [AssessmentProgressLabelComponent], + imports: [ArtemisSharedModule, AssessmentProgressLabelComponent], exports: [AssessmentProgressLabelComponent], }) export class ArtemisAssessmentProgressLabelModule {} diff --git a/src/main/webapp/app/exercises/shared/course-exercises/course-exercise.service.ts b/src/main/webapp/app/exercises/shared/course-exercises/course-exercise.service.ts index c9ea1e512eb3..b467799e3adc 100644 --- a/src/main/webapp/app/exercises/shared/course-exercises/course-exercise.service.ts +++ b/src/main/webapp/app/exercises/shared/course-exercises/course-exercise.service.ts @@ -9,20 +9,18 @@ import { Exercise } from 'app/entities/exercise.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; import { Observable, map } from 'rxjs'; import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { convertDateFromServer } from 'app/utils/date.utils'; import { ProfileService } from 'app/shared/layouts/profiles/profile.service'; @Injectable({ providedIn: 'root' }) export class CourseExerciseService { - private resourceUrl = `api/courses`; + private http = inject(HttpClient); + private participationWebsocketService = inject(ParticipationWebsocketService); + private accountService = inject(AccountService); + private profileService = inject(ProfileService); - constructor( - private http: HttpClient, - private participationWebsocketService: ParticipationWebsocketService, - private accountService: AccountService, - private profileService: ProfileService, - ) {} + private resourceUrl = `api/courses`; /** * returns all programming exercises for the course corresponding to courseId diff --git a/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.html b/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.html index b1b2e9295b76..fbfe3529f40c 100644 --- a/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.html +++ b/src/main/webapp/app/exercises/shared/dashboards/tutor/exercise-assessment-dashboard.component.html @@ -258,7 +258,9 @@

    -
    + + +
    @if (tutorParticipationStatus === REVIEWED_INSTRUCTIONS && !isTestRun) {

    + + + + + + + + + + + + + + + + + + @if ((isTestRun && complaints.length === 0) || !isTestRun) { - - @if (submission && calculateSubmissionStatusIsDraft(submission, correctionRound)) { + + + + + + + + + + + + + + + @if (submission && calculateSubmissionStatusIsDraft(submission, correctionRound)) { + +  {{ + 'artemisApp.exerciseAssessmentDashboard.continueAssessment' | artemisTranslate + }} + + } @else { + @if (!!submission.results && !!submission.results[correctionRound]) { -  {{ - 'artemisApp.exerciseAssessmentDashboard.continueAssessment' | artemisTranslate - }} + +  {{ 'artemisApp.exerciseAssessmentDashboard.openAssessment' | artemisTranslate }} - } @else { - @if (!!submission.results && !!submission.results[correctionRound]) { - - -  {{ 'artemisApp.exerciseAssessmentDashboard.openAssessment' | artemisTranslate }} - - } } - + } + }
    - @if (orionState.view === ExerciseView.INSTRUCTOR && orionState.opened === exercise.id && exercise.isAtLeastEditor && exercise.templateParticipation) { - - } @else { - @if (exercise.isAtLeastEditor && exercise.templateParticipation) { + @if (orionState) { + @if (orionState.view === ExerciseView.INSTRUCTOR && orionState.opened === exercise.id && exercise.isAtLeastEditor && exercise.templateParticipation) { + } @else { + @if (exercise.isAtLeastEditor && exercise.templateParticipation) { + + } } }