Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Communication: Allow users to search for answer post in course wide channels #9616

Closed

Conversation

cremertim
Copy link
Contributor

@cremertim cremertim commented Oct 28, 2024

Checklist

General

Server

  • Important: I implemented the changes with a very good performance and prevented too many (unnecessary) and too complex database calls.
  • I strictly followed the principle of data economy for all database calls.
  • I strictly followed the server coding and design guidelines.
  • I added multiple integration tests (Spring) related to the features (with a high test coverage).
  • I added pre-authorization annotations according to the guidelines and checked the course groups for all new REST Calls (security).
  • I documented the Java code using JavaDoc style.

Motivation and Context

Currently, the search only filters the content of the original post and also excludes private conversations a user migh have

Description

I removed the private channel check and added answer posts, where the original post does not match the content but an answer post matches, to the search result

Steps for Testing

Prerequisites:

  • 1 Student
  • 1 Course
  1. Log in to Artemis
  2. Navigate into the course
  3. If not already existent, create a post with an answer post
  4. Create another post in a direct / group chat with the same content (can also be an answer to a private chat)
  5. Search for the content of the answer post
  6. Verify search result includes answer post

Testserver States

Note

These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Click on the badges to get to the test servers.







Review Progress

Performance Review

  • I (as a reviewer) confirm that the client changes (in particular related to REST calls and UI responsiveness) are implemented with a very good performance even for very large courses with more than 2000 students.
  • I (as a reviewer) confirm that the server changes (in particular related to database calls) are implemented with a very good performance even for very large courses with more than 2000 students.

Code Review

  • Code Review 1
  • Code Review 2

Manual Tests

  • Test 1
  • Test 2

Exam Mode Test

  • Test 1
  • Test 2

Performance Tests

  • Test 1
  • Test 2

Test Coverage

Screenshots

Summary by CodeRabbit

  • New Features

    • Enhanced search functionality to include filtering for both messages and associated answers.
    • Simplified criteria for fetching course-wide channels, focusing on course linkage.
    • Improved filtering for user posts and their associated answers.
    • Added support for managing FAQs within courses, including dynamic retrieval and display of FAQs.
    • Integrated FAQ references into the text editor, allowing users to easily insert FAQ links.
    • Enhanced localization support for FAQs in the application.
  • Bug Fixes

    • Improved error handling during course loading, redirecting unauthorized users appropriately.
  • Tests

    • Expanded test coverage for FAQ functionalities and interactions across various components.

cremertim and others added 30 commits October 22, 2024 17:46
…ssages

# Conflicts:
#	src/main/webapp/app/overview/course-faq/course-faq.component.html
#	src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts
@github-actions github-actions bot added server Pull requests that update Java code. (Added Automatically!) communication Pull requests that affect the corresponding module labels Oct 28, 2024
Copy link

coderabbitai bot commented Oct 28, 2024

Walkthrough

The changes in this pull request enhance the functionality of the MessageSpecs class, specifically updating the getSearchTextSpecification, getCourseWideChannelsSpecification, and getOwnSpecification methods. The modifications allow for broader filtering of messages and associated answer posts, simplifying some logic and focusing on relevant criteria without altering method signatures or overall class structure.

Changes

File Path Change Summary
src/main/java/de/tum/cit/aet/artemis/communication/repository/MessageSpecs.java - Enhanced getSearchTextSpecification to filter both Post and AnswerPost entities.
- Simplified getCourseWideChannelsSpecification to focus on course linkage only.
- Modified getOwnSpecification to include both user posts and associated answer posts.
src/main/webapp/app/entities/course.model.ts - Added import for Faq model.
- Introduced `isFaqEnabled(course: Course
src/main/webapp/app/overview/course-faq/course-faq.component.html - Removed class attribute from <hr> element.
- Wrapped <jhi-course-faq-accordion> in a new <div> element.
src/main/webapp/app/overview/course-faq/course-faq.component.ts - Added imports for ElementRef, effect, viewChildren, SortService, and Renderer2.
- Introduced referencedFaqId property and updated ngOnInit to handle query parameters.
- Added scrollToFaq method and modified loadFaqs to call sortFaqs.
src/main/webapp/app/overview/course-overview.component.ts - Added imports for Faq and FaqService.
- Introduced setFaqs method to fetch FAQs for a course.
- Enhanced error handling in loadCourse method.
src/main/webapp/app/shared/metis/metis.service.ts - Added getLinkForFaq() method to generate a router link for FAQs.
src/main/webapp/app/shared/metis/metis.util.ts - Added FAQ = 'FAQ' to ReferenceType enum.
src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts - Added faQuestion icon for ReferenceType.FAQ in referenceIcon method.
src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts - Updated computePostingContentParts method to handle ReferenceType.FAQ and modified regex in getPatternMatches.
src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts - Updated ngOnInit to conditionally include FaqReferenceAction based on isFaqEnabled.
src/main/webapp/i18n/de/metis.json - Added "faq": "FAQ" key in the editor section.
src/main/webapp/i18n/en/metis.json - Added "faq": "FAQ" key in the editor section.
src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts - Added tests for sorting FAQs and scrolling to specific FAQ elements.
src/test/javascript/spec/component/shared/metis/posting-content/posting-content.component.spec.ts - Added test for handling FAQ references in posting content.
src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts - Enhanced tests for handling FAQ feature in the markdown editor.
src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-communication-action.integration.spec.ts - Integrated FaqReferenceAction into tests for the Monaco editor.
src/test/javascript/spec/helpers/mocks/service/mock-metis-service.service.ts - Added getLinkForFaq method to MockMetisService.
src/test/javascript/spec/helpers/sample/metis-sample-data.ts - Introduced metisFaq1, metisFaq2, metisFaq3 constants and added to metisCourse.
src/test/javascript/spec/service/metis/metis.service.spec.ts - Added test for getLinkForFaq method in MetisService.

Possibly related PRs

Suggested labels

tests, client, feature, ready to merge

Suggested reviewers

  • florian-glombik
  • sarpsahinalp
  • JohannesStoehr
  • pzdr7
  • rabeatwork

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (3)
src/main/java/de/tum/cit/aet/artemis/communication/repository/ConversationMessageRepository.java (2)

73-75: Consider using Optional for null safety.

The null check is good, but consider using Java's Optional for more robust null handling:

-String searchText = postContextFilter.searchText() != null ? postContextFilter.searchText() : "";
+String searchText = Optional.ofNullable(postContextFilter.searchText()).orElse("");

Line range hint 1-123: Consider implementing full-text search for better scalability.

While the current implementation adds search capabilities, consider these architectural improvements for better scalability:

  1. Implement full-text search using database-specific features (e.g., PostgreSQL's tsvector) or dedicated search engines like Elasticsearch for:

    • Better performance with large datasets
    • Advanced search features (fuzzy matching, relevance scoring)
    • Language-aware search capabilities
  2. Consider implementing search result caching to improve performance for frequent searches.

  3. Add search result highlighting to improve user experience.

src/main/java/de/tum/cit/aet/artemis/communication/repository/MessageSpecs.java (1)

95-99: Architecture Advice: Consider implementing separate specifications for different channel types.

Instead of removing predicates, consider implementing separate specifications for different channel types (public/private) to maintain the Single Responsibility Principle and improve code maintainability.

Consider refactoring the specifications into:

  1. getPublicChannelsSpecification
  2. getPrivateChannelsSpecification
  3. getAllChannelsSpecification

This approach would:

  • Maintain clear separation of concerns
  • Make the code more maintainable
  • Allow for better access control
  • Improve testability

Would you like me to provide a detailed implementation for these separate specifications?

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between c442c93 and 47c6d5d.

📒 Files selected for processing (2)
  • src/main/java/de/tum/cit/aet/artemis/communication/repository/ConversationMessageRepository.java (3 hunks)
  • src/main/java/de/tum/cit/aet/artemis/communication/repository/MessageSpecs.java (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
src/main/java/de/tum/cit/aet/artemis/communication/repository/ConversationMessageRepository.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

src/main/java/de/tum/cit/aet/artemis/communication/repository/MessageSpecs.java (1)

Pattern src/main/java/**/*.java: naming:CamelCase; principles:{single_responsibility,small_methods,no_duplication}; db:{perf_queries,datetime_not_timestamp}; rest:{stateless,singleton,delegate_logic,http_only,minimal_dtos}; dtos:{java_records,no_entities,min_data,single_resp}; di:constructor_injection; kiss:simple_code; file_handling:os_indep_paths; practices:{least_access,avoid_transactions,code_reuse,static_member_ref,prefer_primitives}; sql:{param_annotation,uppercase,avoid_subqueries};java:avoid_star_imports

🔇 Additional comments (3)
src/main/java/de/tum/cit/aet/artemis/communication/repository/ConversationMessageRepository.java (2)

94-101: LGTM! Good performance monitoring.

The method maintains good performance tracking with TimeLogUtil and proper logging while incorporating the new search functionality.


121-123: ⚠️ Potential issue

Address SQL performance and security concerns.

The current implementation has several potential issues:

  1. Using LIKE %:searchText% with wildcards on both sides prevents the use of indexes, potentially impacting performance on large datasets.
  2. Case sensitivity depends on database collation and might not meet user expectations.
  3. The query might be vulnerable to SQL wildcards in user input.

Consider these improvements:

  1. For better performance:
-- Use OR with LOWER() and indexes
WHERE p.id IN :postIds 
AND (
    LOWER(p.content) LIKE LOWER(CONCAT('%', :searchText, '%'))
    OR LOWER(a.content) LIKE LOWER(CONCAT('%', :searchText, '%'))
)
  1. For security, escape SQL wildcards in the service layer:
searchText = searchText.replace("%", "\\%").replace("_", "\\_");

Let's verify if there are any existing full-text search indexes:

src/main/java/de/tum/cit/aet/artemis/communication/repository/MessageSpecs.java (1)

95-99: ⚠️ Potential issue

Security and Performance Concerns: Reconsider removing channel filtering predicates.

The removal of isCourseWidePredicate and isChannelPredicate raises several concerns:

  1. Security: Removing the course-wide channel check might expose messages from private channels that should remain restricted.
  2. Performance: Without type filtering (isChannelPredicate), the query might fetch unnecessary conversation subtypes, impacting query performance.
  3. Data Integrity: The specification no longer ensures we're working with proper channel types.

Restore the predicates with proper access control:

    public static Specification<Post> getCourseWideChannelsSpecification(Long courseId) {
        return (root, query, criteriaBuilder) -> {
            final var conversationJoin = root.join(Post_.conversation, JoinType.LEFT);
            final var isInCoursePredicate = criteriaBuilder.equal(conversationJoin.get(Channel_.COURSE).get(Course_.ID), courseId);
-           // final var isCourseWidePredicate = criteriaBuilder.isTrue(conversationJoin.get(Channel_.IS_COURSE_WIDE));
-           // make sure we only fetch channels (which are sub types of conversations)
-           // this avoids the creation of sub queries
-           // final var isChannelPredicate = criteriaBuilder.equal(conversationJoin.type(), criteriaBuilder.literal(Channel.class));
-           return criteriaBuilder.and(isInCoursePredicate);
+           final var isCourseWidePredicate = criteriaBuilder.isTrue(conversationJoin.get(Channel_.IS_COURSE_WIDE));
+           // make sure we only fetch channels (which are sub types of conversations)
+           // this avoids the creation of sub queries
+           final var isChannelPredicate = criteriaBuilder.equal(conversationJoin.type(), criteriaBuilder.literal(Channel.class));
+           return criteriaBuilder.and(isInCoursePredicate, isChannelPredicate);
        };
    }

Let's verify the impact of these changes on the codebase:

@github-actions github-actions bot added the client Pull requests that update TypeScript code. (Added Automatically!) label Oct 28, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (3)
src/main/webapp/i18n/en/conversation.json (1)

48-51: Consider adding tooltips for new conversation types.

To enhance user experience, consider adding tooltip translations to explain the difference between group chats and direct messages.

Add these entries under the existing structure:

                "groupChat": "Group Chat",
                "groupChats": "Group Chats",
+               "groupChatTooltip": "Chat with multiple selected participants",
                "directMessage": "Direct Message",
                "directMessages": "Direct Messages",
+               "directMessageTooltip": "Private conversation between two people"
src/main/webapp/i18n/de/conversation.json (1)

48-51: LGTM with a minor suggestion for consistency.

The new German translations for group chats and direct messages are grammatically correct and follow proper German capitalization rules. The translations fit well with the existing entries and support the new search functionality.

Consider adding tooltips or descriptions for these new conversation types, similar to how other UI elements have descriptive text. This would help users understand the difference between these communication channels. For example:

 "conversationSelectionSideBar": {
     "groupChat": "Gruppenchat",
     "groupChats": "Gruppenchats",
     "directMessage": "Direktnachricht",
     "directMessages": "Direktnachrichten",
+    "groupChatDescription": "Chat zwischen dir und mehreren anderen Nutzer:innen",
+    "directMessageDescription": "Privater Chat zwischen dir und einer anderen Person"
src/main/webapp/app/shared/metis/metis.service.ts (1)

53-53: Consider using constructor injection instead of inject function.

While using the inject function is valid, constructor injection is the more conventional approach in Angular services and provides better testability.

-    private translateService = inject(TranslateService);
+    constructor(
+        protected postService: PostService,
+        protected answerPostService: AnswerPostService,
+        protected reactionService: ReactionService,
+        protected accountService: AccountService,
+        protected exerciseService: ExerciseService,
+        private jhiWebsocketService: JhiWebsocketService,
+        private conversationService: ConversationService,
+        notificationService: NotificationService,
+        private translateService: TranslateService,
+    ) {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f1562b2 and dbe5e5f.

📒 Files selected for processing (4)
  • src/main/java/de/tum/cit/aet/artemis/communication/repository/MessageSpecs.java (2 hunks)
  • src/main/webapp/app/shared/metis/metis.service.ts (4 hunks)
  • src/main/webapp/i18n/de/conversation.json (1 hunks)
  • src/main/webapp/i18n/en/conversation.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/de/tum/cit/aet/artemis/communication/repository/MessageSpecs.java
🧰 Additional context used
📓 Path-based instructions (2)
src/main/webapp/app/shared/metis/metis.service.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/i18n/de/conversation.json (1)

Pattern src/main/webapp/i18n/de/**/*.json: German language translations should be informal (dutzen) and should never be formal (sietzen). So the user should always be addressed with "du/dein" and never with "sie/ihr".

🔇 Additional comments (2)
src/main/webapp/i18n/en/conversation.json (1)

48-51: LGTM! The new translation keys are well-structured.

The additions follow good i18n practices with clear, consistent naming and proper singular/plural forms.

Let's verify the corresponding German translations exist:

✅ Verification successful

German translations are properly synchronized with the English ones

The German translations for all new keys are present and correctly formatted:

  • "groupChat" → "Gruppenchat"
  • "groupChats" → "Gruppenchats"
  • "directMessage" → "Direktnachricht"
  • "directMessages" → "Direktnachrichten"
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the German translations exist for the new keys
# Expected: Should find matching entries in the German translation file

rg -A 1 '"groupChat"|"directMessage"' 'src/main/webapp/i18n/de/conversation.json'

Length of output: 278

src/main/webapp/app/shared/metis/metis.service.ts (1)

30-34: LGTM: Clean import organization.

The new imports for conversation types and TranslateService are properly organized and follow Angular's style guide.

src/main/webapp/app/shared/metis/metis.service.ts Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (1)
src/main/webapp/i18n/en/conversation.json (1)

48-51: LGTM! Consider documenting the usage context.

The new translation keys for singular forms ("groupChat" and "directMessage") complement the existing plural forms well. This distinction suggests they're used in different UI contexts (e.g., singular for labels, plural for section headers).

Consider adding a code comment in the JSON to document where these singular forms are used in the UI, as it would help other developers understand the distinction between singular and plural forms:

 "conversationSelectionSideBar": {
+    // Singular forms used for individual chat labels
     "groupChat": "Group Chat",
     "groupChats": "Group Chats",
     "directMessage": "Direct Message",
     "directMessages": "Direct Messages",
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f1562b2 and dbe5e5f.

📒 Files selected for processing (4)
  • src/main/java/de/tum/cit/aet/artemis/communication/repository/MessageSpecs.java (2 hunks)
  • src/main/webapp/app/shared/metis/metis.service.ts (4 hunks)
  • src/main/webapp/i18n/de/conversation.json (1 hunks)
  • src/main/webapp/i18n/en/conversation.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/de/tum/cit/aet/artemis/communication/repository/MessageSpecs.java
🧰 Additional context used
📓 Path-based instructions (2)
src/main/webapp/app/shared/metis/metis.service.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/i18n/de/conversation.json (1)

Pattern src/main/webapp/i18n/de/**/*.json: German language translations should be informal (dutzen) and should never be formal (sietzen). So the user should always be addressed with "du/dein" and never with "sie/ihr".

🔇 Additional comments (3)
src/main/webapp/i18n/en/conversation.json (1)

48-51: Verify translation consistency across languages.

The changes look good, but we should ensure these new keys are consistently added across all language files.

✅ Verification successful

Let me gather more information to properly verify the translation consistency.


Let me try one more verification with a different approach to check the files' content.


Translation consistency verified across all language files

The new keys "groupChat", "groupChats", "directMessage", and "directMessages" are properly added and translated in both English and German language files under the conversationSelectionSideBar section. The translations are consistent and complete, with no missing keys.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the new keys exist in all language files
# Expected: Both keys should exist in all i18n JSON files

echo "Checking for new translation keys across all language files..."
for lang_file in $(find src/main/webapp/i18n -name 'conversation.json'); do
    echo "Checking $lang_file..."
    jq -r '.artemisApp.conversationSelectionSideBar | select(.groupChat and .directMessage)' "$lang_file" || echo "Missing keys in $lang_file"
done

Length of output: 835


Script:

#!/bin/bash
# Let's check the actual content of both conversation.json files to see the translation keys
echo "=== Content of English translation file ==="
jq '.artemisApp.conversationSelectionSideBar' src/main/webapp/i18n/en/conversation.json

echo -e "\n=== Content of German translation file ==="
jq '.artemisApp.conversationSelectionSideBar' src/main/webapp/i18n/de/conversation.json

# Let's also check if there are any other language files we might have missed
echo -e "\n=== All available language directories ==="
ls src/main/webapp/i18n/

Length of output: 517


Script:

#!/bin/bash
# Let's check the raw content of both files to understand the structure
echo "=== Content of English translation file ==="
cat src/main/webapp/i18n/en/conversation.json

echo -e "\n=== Content of German translation file ==="
cat src/main/webapp/i18n/de/conversation.json

Length of output: 27309

src/main/webapp/i18n/de/conversation.json (1)

48-51: LGTM! The translations are accurate and follow guidelines.

The new German translations for group chat and direct message features:

  • Use the correct informal tone (dutzen)
  • Maintain consistency with existing translations
  • Follow common German tech terminology
  • Include proper plural forms
src/main/webapp/app/shared/metis/metis.service.ts (1)

9-9: LGTM! Clean import organization.

The imports are well-organized and follow Angular's style guidelines.

Also applies to: 30-34

src/main/webapp/app/shared/metis/metis.service.ts Outdated Show resolved Hide resolved
src/main/webapp/app/shared/metis/metis.service.ts Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between dbe5e5f and ffbac97.

📒 Files selected for processing (1)
  • src/main/webapp/app/shared/metis/metis.service.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/main/webapp/app/shared/metis/metis.service.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

@ls1intum ls1intum deleted a comment from coderabbitai bot Oct 29, 2024
@ls1intum ls1intum deleted a comment from coderabbitai bot Oct 29, 2024
@cremertim cremertim changed the title Communication: Allow users to search for answer post and private messages Communication: Allow users to search for answer post Oct 29, 2024
@cremertim cremertim changed the title Communication: Allow users to search for answer post Communication: Allow users to search for answer post in course wide channels Oct 29, 2024
@github-actions github-actions bot removed the client Pull requests that update TypeScript code. (Added Automatically!) label Oct 29, 2024
coderabbitai[bot]
coderabbitai bot previously approved these changes Oct 29, 2024
@github-actions github-actions bot added tests client Pull requests that update TypeScript code. (Added Automatically!) labels Oct 29, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Outside diff range and nitpick comments (12)
src/test/javascript/spec/helpers/mocks/service/mock-metis-service.service.ts (1)

97-99: LGTM! Consider adding test coverage.

The implementation is consistent with other link generation methods in the class and correctly mocks the real service's behavior.

Consider adding a test case to verify the mock's behavior:

describe('MockMetisService', () => {
    let service: MockMetisService;

    beforeEach(() => {
        service = new MockMetisService();
    });

    it('should generate correct FAQ link', () => {
        const course = service.getCourse();
        expect(service.getLinkForFaq()).toBe(`/courses/${course.id}/faq`);
    });
});
src/main/webapp/app/shared/metis/metis.util.ts (1)

Line range hint 82-111: Consider adding documentation for ReferenceType enum.

While interfaces like PostingContentPart and PatternMatch are well documented, the ReferenceType enum lacks documentation explaining the purpose of each type and when to use them.

Add JSDoc comments like this:

+/**
+ * Defines the types of content that can be referenced within the application.
+ * Used for routing, rendering, and content processing logic.
+ */
 export enum ReferenceType {
+    /** Regular discussion posts */
     POST = 'POST',
+    /** Course lecture content */
     LECTURE = 'LECTURE',
     // ... add documentation for other types
+    /** Frequently Asked Questions */
     FAQ = 'FAQ',
     IMAGE = 'IMAGE',
 }
src/main/webapp/app/entities/course.model.ts (1)

32-34: Add JSDoc comment for consistency.

The function implementation is correct and follows the established patterns. Consider adding a JSDoc comment similar to other utility functions in this file for better documentation.

+/**
+ * Checks if FAQ feature is enabled for the given course
+ * Note: Keep in sync with method in CourseRepository.java
+ */
export function isFaqEnabled(course: Course | undefined) {
    return course?.faqEnabled;
}
src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (1)

Line range hint 44-57: Consider adding accessibility attributes for FAQ references.

Since we're adding FAQ reference functionality to the markdown editor, ensure that:

  1. The FAQ reference button has proper ARIA labels
  2. The FAQ selection interface is keyboard navigable
  3. Screen readers can properly announce FAQ selections

This will improve the user experience for users relying on assistive technologies.

src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts (2)

171-183: Consider enhancing scroll test coverage

While the happy path is well tested, consider adding the following improvements:

  1. Verify that focus() is called after scrollIntoView
  2. Add a test case for when the FAQ ID doesn't exist in the list

Example addition:

it('should handle non-existent faq id gracefully', () => {
    const nativeElement = { id: 'faq-1', scrollIntoView: jest.fn(), focus: jest.fn() };
    const elementRef = new ElementRef(nativeElement);
    
    courseFaqComponent.faqElements = signal([elementRef]);
    
    courseFaqComponent.scrollToFaq(999); // non-existent id
    
    expect(nativeElement.scrollIntoView).not.toHaveBeenCalled();
    expect(nativeElement.focus).not.toHaveBeenCalled();
});

182-182: Consider using more specific jest matchers

According to the coding guidelines, prefer using specific jest matchers. For scrollIntoView verification, consider using toHaveBeenCalledExactlyOnceWith instead of toHaveBeenCalledWith to be more explicit about the expected number of calls.

-expect(nativeElement1.scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth', block: 'start' });
+expect(nativeElement1.scrollIntoView).toHaveBeenCalledExactlyOnceWith({ behavior: 'smooth', block: 'start' });
src/test/javascript/spec/helpers/sample/metis-sample-data.ts (1)

42-44: Enhance test data diversity for FAQ samples.

The current FAQ test data uses identical values for questionTitle and questionAnswer across all samples. Consider using more diverse and realistic test data to cover different scenarios and edge cases.

Consider this improvement:

-export const metisFaq1 = { id: 1, questionTitle: 'title', questionAnswer: 'answer' };
-export const metisFaq2 = { id: 2, questionTitle: 'title', questionAnswer: 'answer' };
-export const metisFaq3 = { id: 3, questionTitle: 'title', questionAnswer: 'answer' };
+export const metisFaq1 = { id: 1, questionTitle: 'How to submit assignments?', questionAnswer: 'Click the Submit button on the assignment page.' };
+export const metisFaq2 = { id: 2, questionTitle: 'Where to find course materials?', questionAnswer: 'All materials are available in the Resources section.' };
+export const metisFaq3 = { id: 3, questionTitle: 'What is the grading policy?', questionAnswer: 'Assignments: 40%, Exams: 60%' };
src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts (1)

Line range hint 1-250: Consider extracting reference parsing logic to a dedicated service.

The component is handling multiple responsibilities with complex string parsing logic. Consider these architectural improvements:

  1. Extract reference parsing to a dedicated service:
@Injectable()
export class ReferenceParserService {
    parseReference(content: string, type: ReferenceType): ParsedReference {
        // Move parsing logic here
    }
}
  1. Create type-safe interfaces for parsed references:
interface ParsedReference {
    text: string;
    link?: string[];
    queryParams?: Params;
}

This would:

  • Improve testability by isolating parsing logic
  • Make the component more focused on its primary responsibility
  • Allow reuse of parsing logic in other components
  • Make it easier to add new reference types
src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts (1)

42-42: LGTM: Proper spy setup with a minor suggestion.

The spy setup follows best practices for HTTP mocking. Consider being more explicit about the return type for better type safety.

-                const returnValue = of(new HttpResponse({ body: [], status: 200 }));
+                const returnValue = of(new HttpResponse<any[]>({ body: [], status: 200 }));

Also applies to: 123-126

src/test/javascript/spec/service/metis/metis.service.spec.ts (1)

333-337: LGTM! The test case is well-structured and follows established patterns.

The test case follows good testing practices:

  • Clear and descriptive test name
  • Proper setup with course context
  • Focused assertion testing a single behavior

Consider extracting the expected URL path to a constant at the top of the file, similar to how other test data is managed:

+const FAQ_PATH = '/faq';
+
 describe('Metis Service', () => {
     // ... existing code ...
     it('should determine the router link required for referencing a faq', () => {
         metisService.setCourse(course);
         const link = metisService.getLinkForFaq();
-        expect(link).toBe(`/courses/${metisCourse.id}/faq`);
+        expect(link).toBe(`/courses/${metisCourse.id}${FAQ_PATH}`);
     });
src/main/webapp/app/shared/metis/metis.service.ts (1)

497-500: Fix incorrect JSDoc description.

The JSDoc comment incorrectly mentions "exercise" in the description. It should describe the FAQ navigation purpose instead.

-     * returns the router link required for navigating to the exercise referenced within a faq
+     * returns the router link required for navigating to the FAQ section of a course
src/test/javascript/spec/component/shared/metis/posting-content/posting-content.component.spec.ts (1)

495-509: Consider using fakeAsync for consistency.

The test case is well-structured and correctly verifies FAQ reference handling. However, for consistency with other similar test cases in this file, consider wrapping it with fakeAsync.

-        it('should compute parts when referencing a faq', () => {
+        it('should compute parts when referencing a faq', fakeAsync(() => {
             component.content = `I want to reference [faq]faq(/courses/1/faq?faqId=45)[/faq].`;
             const matches = component.getPatternMatches();
             component.computePostingContentParts(matches);
             expect(component.postingContentParts()).toEqual([
                 {
                     contentBeforeReference: 'I want to reference ',
                     linkToReference: ['/courses/1/faq'],
                     referenceStr: `faq`,
                     referenceType: ReferenceType.FAQ,
                     contentAfterReference: '.',
                     queryParams: { faqId: '45' },
                 } as PostingContentPart,
             ]);
-        });
+        }));
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 676d211 and 17755b8.

📒 Files selected for processing (19)
  • src/main/webapp/app/entities/course.model.ts (3 hunks)
  • src/main/webapp/app/overview/course-faq/course-faq.component.html (1 hunks)
  • src/main/webapp/app/overview/course-faq/course-faq.component.ts (6 hunks)
  • src/main/webapp/app/overview/course-overview.component.ts (4 hunks)
  • src/main/webapp/app/shared/metis/metis.service.ts (1 hunks)
  • src/main/webapp/app/shared/metis/metis.util.ts (1 hunks)
  • src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (3 hunks)
  • src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts (2 hunks)
  • src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (4 hunks)
  • src/main/webapp/app/shared/monaco-editor/model/actions/communication/faq-reference.action.ts (1 hunks)
  • src/main/webapp/i18n/de/metis.json (1 hunks)
  • src/main/webapp/i18n/en/metis.json (1 hunks)
  • src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts (5 hunks)
  • src/test/javascript/spec/component/shared/metis/posting-content/posting-content.component.spec.ts (1 hunks)
  • src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts (5 hunks)
  • src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-communication-action.integration.spec.ts (9 hunks)
  • src/test/javascript/spec/helpers/mocks/service/mock-metis-service.service.ts (1 hunks)
  • src/test/javascript/spec/helpers/sample/metis-sample-data.ts (1 hunks)
  • src/test/javascript/spec/service/metis/metis.service.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (18)
src/main/webapp/app/entities/course.model.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/overview/course-faq/course-faq.component.html (1)

Pattern src/main/webapp/**/*.html: @if and @for are new and valid Angular syntax replacing *ngIf and *ngFor. They should always be used over the old style.

src/main/webapp/app/overview/course-faq/course-faq.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/overview/course-overview.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/metis.service.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/metis.util.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/app/shared/monaco-editor/model/actions/communication/faq-reference.action.ts (1)

Pattern src/main/webapp/**/*.ts: angular_style:https://angular.io/guide/styleguide;methods_in_html:false;lazy_loading:true;code_reuse:true;tests:meaningful;types:PascalCase;enums:PascalCase;funcs:camelCase;props:camelCase;no_priv_prefix:true;strings:single_quotes;localize:true;btns:functionality;links:navigation;icons_text:newline;labels:associate;code_style:arrow_funcs,curly_braces,open_braces_same_line,indent_4;memory_leak_prevention:true;routes:naming_schema;chart_framework:ngx-charts;responsive_layout:true

src/main/webapp/i18n/de/metis.json (1)

Pattern src/main/webapp/i18n/de/**/*.json: German language translations should be informal (dutzen) and should never be formal (sietzen). So the user should always be addressed with "du/dein" and never with "sie/ihr".

src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/component/shared/metis/posting-content/posting-content.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-communication-action.integration.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/helpers/mocks/service/mock-metis-service.service.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/helpers/sample/metis-sample-data.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

src/test/javascript/spec/service/metis/metis.service.spec.ts (1)

Pattern src/test/javascript/spec/**/*.ts: jest: true; mock: NgMocks; bad_practices: avoid_full_module_import; perf_improvements: mock_irrelevant_deps; service_testing: mock_http_for_logic; no_schema: avoid_NO_ERRORS_SCHEMA; expectation_specificity: true; solutions: {boolean: toBeTrue/False, reference: toBe, existence: toBeNull/NotNull, undefined: toBeUndefined, class_obj: toContainEntries/toEqual, spy_calls: {not_called: not.toHaveBeenCalled, once: toHaveBeenCalledOnce, with_value: toHaveBeenCalledWith|toHaveBeenCalledExactlyOnceWith}}

🔇 Additional comments (30)
src/main/webapp/app/overview/course-faq/course-faq.component.html (3)

29-29: Verify the visual impact of removing margin classes.

The removal of mb-1 mt-1 classes from the <hr> element might affect the spacing and visual hierarchy. Please ensure this doesn't negatively impact the layout and readability of the FAQ sections.


35-37: LGTM! Clean implementation of FAQ element references.

The addition of template references and unique IDs for FAQ elements is well-structured and follows Angular best practices. This enables programmatic scrolling and manipulation of individual FAQ elements.


Line range hint 8-26: LGTM! Excellent use of new Angular control flow syntax.

The implementation correctly uses the new @if and @for syntax as recommended by the coding guidelines. The proper use of tracking in @for loops is also commendable for performance optimization.

Also applies to: 31-43

src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts (2)

18-18: LGTM: Import follows Angular style guidelines.

The faQuestion import is properly grouped with other FontAwesome imports and maintains alphabetical ordering.


47-47: LGTM: Icon declaration follows component patterns.

The faQuestion icon declaration maintains consistency with other icon declarations and follows proper access patterns.

src/main/webapp/app/shared/metis/metis.util.ts (1)

109-109: LGTM! Verify integration points.

The addition of FAQ to ReferenceType follows the existing enum pattern and naming conventions.

Let's verify the integration points mentioned in the AI summary:

✅ Verification successful

Integration verified successfully

The FAQ enum value is properly integrated across the codebase:

  • Used in posting-content.components.ts for FAQ reference parsing
  • Icon mapping in posting-content-part.components.ts using faQuestion
  • Test coverage in place with proper test cases including FAQ references and query parameters
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that FAQ reference type is properly integrated across components

# Test 1: Check for FAQ handling in posting content components
rg -l "ReferenceType.FAQ" "src/main/webapp/app/shared/metis"

# Test 2: Verify test coverage
rg -l "ReferenceType.FAQ" "src/test/javascript/spec"

Length of output: 391


Script:

#!/bin/bash
# Let's examine the actual usage of ReferenceType.FAQ in these files
# to ensure proper integration

# Check posting-content component implementation
rg "ReferenceType.FAQ" "src/main/webapp/app/shared/metis/posting-content/posting-content.components.ts" -B 2 -A 2

# Check posting-content-part component implementation
rg "ReferenceType.FAQ" "src/main/webapp/app/shared/metis/posting-content/posting-content-part/posting-content-part.components.ts" -B 2 -A 2

# Check test implementation
rg "ReferenceType.FAQ" "src/test/javascript/spec/component/shared/metis/posting-content/posting-content.component.spec.ts" -B 2 -A 2

Length of output: 1416

src/main/webapp/app/entities/course.model.ts (2)

16-16: LGTM: Import statement follows Angular conventions.

The import statement for the Faq model is properly structured and necessary for the new faqs property.


106-106: Verify FAQ initialization in related components.

The property declaration looks good and follows TypeScript/Angular conventions. However, ensure that components using this property handle the undefined case appropriately.

✅ Verification successful

Let me check the initialization context by examining the component code.


Let me check how the faqs are used in the template to ensure proper null handling.


Let me check the FAQ component implementation to verify the complete handling.


Property initialization and usage are properly handled

The verification shows that the faqs property is correctly handled throughout the codebase:

  • Initialization is done safely in course-overview.component.ts through a service call
  • The FAQ component properly initializes and manages the array with null checks in the template
  • Filtering, searching, and sorting operations are properly implemented with null-safe operations
  • The template uses safe navigation operator (?) for null checking
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of course.faqs to verify proper null checking
rg "course\.faqs" --type ts

Length of output: 130


Script:

#!/bin/bash
# Get the component code with context
rg "course\.faqs" -B 5 -A 5 --type ts

Length of output: 1025


Script:

#!/bin/bash
# Search for faqs usage in the template
rg "faqs" --type html

Length of output: 617


Script:

#!/bin/bash
# Get the FAQ component implementation
cat src/main/webapp/app/overview/course-faq/course-faq.component.ts

Length of output: 5724

src/main/webapp/app/shared/metis/posting-markdown-editor/posting-markdown-editor.component.ts (2)

24-24: LGTM: Import statements follow Angular conventions.

The new imports are properly organized and necessary for the FAQ functionality implementation.

Also applies to: 37-37


97-98: 🛠️ Refactor suggestion

Consider optimizing FAQ action initialization.

While the implementation is functionally correct, consider these improvements:

  1. Memoize the FAQ action to avoid unnecessary instance creation:
-  const faqAction = isFaqEnabled(this.metisService.getCourse()) ? [new FaqReferenceAction(this.metisService)] : [];
+  private readonly faqAction = computed(() => 
+    isFaqEnabled(this.metisService.getCourse()) ? [new FaqReferenceAction(this.metisService)] : []
+  );

   ngOnInit(): void {
     // ...
-    ...faqAction,
+    ...this.faqAction(),
  1. Verify if the FaqReferenceAction needs cleanup in ngOnDestroy to prevent memory leaks.

Also applies to: 111-111

src/main/webapp/i18n/en/metis.json (1)

49-50: LGTM! The new FAQ translation entry follows the existing pattern.

The addition of the FAQ translation key is properly structured and maintains consistency with the existing format.

Let's verify the corresponding German translation exists:

✅ Verification successful

Translation key 'faq' is properly synchronized in both English and German localization files

The verification confirms that the German localization file (src/main/webapp/i18n/de/metis.json) contains the corresponding FAQ translation entry with the same value, maintaining consistency across languages.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the FAQ translation exists in the German localization file
# Expected: Should find "faq": "FAQ" in the German file

rg '"faq":\s*"FAQ"' 'src/main/webapp/i18n/de/metis.json'

Length of output: 85

src/test/javascript/spec/component/overview/course-faq/course-faq.component.spec.ts (3)

22-23: LGTM: Service setup follows best practices

The addition of SortService follows the established pattern of service initialization in the test suite.

Also applies to: 41-41, 111-111


71-73: LGTM: Route mock properly configured

The queryParams mock is correctly implemented using an Observable, which matches Angular's ActivatedRoute behavior.


165-169: LGTM: Sort functionality properly tested

The test correctly verifies the interaction with sortService using appropriate jest spies and expectations.

src/main/webapp/i18n/de/metis.json (1)

49-50: LGTM! The translation is correct and follows conventions.

The addition of "FAQ" as the German translation is appropriate as this is the standard term used in German interfaces.

src/test/javascript/spec/helpers/sample/metis-sample-data.ts (1)

53-53: LGTM! Clean integration of FAQs into metisCourse.

The FAQs array is properly integrated into the metisCourse object, maintaining consistency with the existing structure.

src/test/javascript/spec/component/shared/metis/postings-markdown-editor/postings-markdown-editor.component.spec.ts (3)

11-11: LGTM: Import changes are appropriate.

The new imports are necessary for the added functionality and follow the coding guidelines.

Also applies to: 26-26


181-204: LGTM: Comprehensive test for FAQ enabled scenario.

The test properly verifies the presence of FaqReferenceAction when FAQ is enabled.


206-226: LGTM: Comprehensive test for FAQ disabled scenario.

The test properly verifies the absence of FaqReferenceAction when FAQ is disabled.

src/test/javascript/spec/component/shared/monaco-editor/monaco-editor-communication-action.integration.spec.ts (5)

31-32: LGTM! Import and setup changes are consistent with existing patterns.

Also applies to: 47-47


75-75: LGTM! Action initialization is consistent with existing patterns.


186-193: LGTM! Helper function follows existing patterns.

The checkFaqSuggestions function is well-implemented and consistent with other check functions.


214-216: LGTM! Test case execution is consistent with existing patterns.


267-273: LGTM! Initialization test follows existing patterns.

src/test/javascript/spec/service/metis/metis.service.spec.ts (1)

Line range hint 1-585: Verify FAQ feature test coverage.

The test suite has good coverage for the FAQ link generation, but let's verify if we need additional test cases for edge cases.

✅ Verification successful

Let me analyze the test coverage more precisely by looking at the actual FAQ-related functionality in the metis service.


Based on the results, I can now provide a final response regarding the FAQ feature test coverage.

FAQ test coverage is adequate and verified

The implementation of getLinkForFaq() in the metis service is straightforward - it simply returns a URL string using the course ID. The current test case thoroughly verifies this functionality by:

  • Setting up the course context
  • Calling the method
  • Verifying the exact expected URL format

Since the method has no edge cases (it's a simple string template) and the test verifies both the method call and the expected output format, no additional test coverage is needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for FAQ-related functionality in the codebase
echo "Searching for FAQ-related methods and tests..."
rg -l "getFaq|faq" --type ts

# Search for additional test cases related to FAQ
echo "Searching for FAQ-related test cases..."
ast-grep --pattern 'it($_, function() { $$$ faq $$$ })'

Length of output: 2590


Script:

#!/bin/bash
# Look for the FAQ-related method implementation in metis service
echo "Searching for FAQ method implementation in metis service..."
rg -A 5 "getLinkForFaq" src/main/webapp/app/shared/metis/metis.service.ts

# Look for all FAQ-related test cases in metis service spec
echo "Searching for FAQ test cases in metis service spec..."
rg -B 2 -A 5 "getLinkForFaq|faq" src/test/javascript/spec/service/metis/metis.service.spec.ts

Length of output: 1053

src/main/webapp/app/shared/metis/metis.service.ts (1)

501-503: LGTM: Clean implementation of FAQ navigation link.

The implementation follows the established patterns in the service and correctly uses the course context for URL generation.

src/main/webapp/app/overview/course-overview.component.ts (2)

71-72: LGTM: Clean imports and modern dependency injection!

The imports are well-organized, and the use of the inject function for dependency injection follows Angular's modern best practices.

Also applies to: 196-196


723-723: LGTM: Clean integration with existing code!

The setFaqs method is well-integrated:

  • Called at the right time after course is set
  • Respects the FAQ feature flag
  • Follows the established patterns for sidebar items

Also applies to: 723-723

src/main/webapp/app/shared/monaco-editor/model/actions/communication/faq-reference.action.ts (2)

64-67: LGTM!

The dispose method correctly disposes of the completion provider, preventing potential memory leaks.


69-71: Verify the correctness of the opening identifier.

The getOpeningIdentifier method returns '[faq]'. Ensure that this identifier aligns with the expected syntax in the editor's parsing logic and functions correctly within the application.

Comment on lines +104 to +105
case ReferenceType.FAQ:
return faQuestion;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider extracting icon mappings to improve maintainability.

While the FAQ case is correctly implemented, the referenceIcon method is growing with many cases. Consider extracting the icon mappings to a constant object for better maintainability.

Here's a suggested refactoring:

private static readonly REFERENCE_TYPE_ICONS: Record<ReferenceType, IconProp> = {
    [ReferenceType.POST]: faMessage,
    [ReferenceType.LECTURE]: faChalkboardUser,
    [ReferenceType.PROGRAMMING]: faKeyboard,
    [ReferenceType.MODELING]: faProjectDiagram,
    [ReferenceType.QUIZ]: faCheckDouble,
    [ReferenceType.TEXT]: faFont,
    [ReferenceType.FILE_UPLOAD]: faFileUpload,
    [ReferenceType.SLIDE]: faFile,
    [ReferenceType.FAQ]: faQuestion
};

referenceIcon(reference: ReferenceType): IconProp {
    return PostingContentPartComponent.REFERENCE_TYPE_ICONS[reference] ?? faPaperclip;
}

This approach:

  • Reduces method complexity
  • Makes it easier to add new mappings
  • Improves type safety
  • Follows Angular's style guidelines

@@ -206,9 +212,10 @@ export class PostingContentComponent implements OnInit, OnChanges, OnDestroy {
// Group 8: reference pattern for Lecture Units
// Group 9: reference pattern for Users
// Group 10: pattern for embedded images
// Group 11: reference pattern for FAQ
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider improving regex maintainability and validation.

The regex pattern could benefit from these improvements:

  1. Split the long regex into named constants for better maintainability:
private static readonly PATTERNS = {
    POST: '#\\d+',
    FAQ: '\\[faq].*?\\[\\/faq]',
    // ... other patterns
};

private static readonly COMBINED_PATTERN = new RegExp(
    Object.entries(PATTERNS)
        .map(([key, pattern]) => `(?<${key}>${pattern})`)
        .join('|'),
    'g'
);
  1. Make the FAQ pattern more specific to validate the expected format:
FAQ: '\\[faq]\\s*([^\\[\\]]+?)\\s*\\(\\s*\\/courses\\/[^\\s]+\\?faqId=\\d+\\s*\\)\\s*\\[\\/faq]'

Also applies to: 218-218

Comment on lines +120 to +125
} else if (ReferenceType.FAQ === referenceType) {
referenceStr = this.content.substring(this.content.indexOf(']', patternMatch.startIndex)! + 1, this.content.indexOf('(/courses', patternMatch.startIndex)!);
linkToReference = [
this.content.substring(this.content.indexOf('(/courses', patternMatch.startIndex)! + 1, this.content.indexOf('?faqId', patternMatch.startIndex)),
];
queryParams = { faqId: this.content.substring(this.content.indexOf('=') + 1, this.content.indexOf(')')) } as Params;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null checks and improve string parsing robustness.

The FAQ reference parsing logic could be more resilient to malformed input. Consider these improvements:

 } else if (ReferenceType.FAQ === referenceType) {
-    referenceStr = this.content.substring(this.content.indexOf(']', patternMatch.startIndex)! + 1, this.content.indexOf('(/courses', patternMatch.startIndex)!);
-    linkToReference = [
-        this.content.substring(this.content.indexOf('(/courses', patternMatch.startIndex)! + 1, this.content.indexOf('?faqId', patternMatch.startIndex)),
-    ];
-    queryParams = { faqId: this.content.substring(this.content.indexOf('=') + 1, this.content.indexOf(')')) } as Params;
+    const closingBracketIndex = this.content.indexOf(']', patternMatch.startIndex);
+    const coursePathIndex = this.content.indexOf('(/courses', patternMatch.startIndex);
+    const faqIdIndex = this.content.indexOf('?faqId', patternMatch.startIndex);
+    const equalsIndex = this.content.indexOf('=', faqIdIndex);
+    const closingParenIndex = this.content.indexOf(')', equalsIndex);
+    
+    if (closingBracketIndex === -1 || coursePathIndex === -1 || faqIdIndex === -1 || 
+        equalsIndex === -1 || closingParenIndex === -1) {
+        console.warn('Malformed FAQ reference detected');
+        return;
+    }
+    
+    referenceStr = this.content.substring(closingBracketIndex + 1, coursePathIndex);
+    linkToReference = [this.content.substring(coursePathIndex + 1, faqIdIndex)];
+    queryParams = { faqId: this.content.substring(equalsIndex + 1, closingParenIndex) } as Params;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (ReferenceType.FAQ === referenceType) {
referenceStr = this.content.substring(this.content.indexOf(']', patternMatch.startIndex)! + 1, this.content.indexOf('(/courses', patternMatch.startIndex)!);
linkToReference = [
this.content.substring(this.content.indexOf('(/courses', patternMatch.startIndex)! + 1, this.content.indexOf('?faqId', patternMatch.startIndex)),
];
queryParams = { faqId: this.content.substring(this.content.indexOf('=') + 1, this.content.indexOf(')')) } as Params;
} else if (ReferenceType.FAQ === referenceType) {
const closingBracketIndex = this.content.indexOf(']', patternMatch.startIndex);
const coursePathIndex = this.content.indexOf('(/courses', patternMatch.startIndex);
const faqIdIndex = this.content.indexOf('?faqId', patternMatch.startIndex);
const equalsIndex = this.content.indexOf('=', faqIdIndex);
const closingParenIndex = this.content.indexOf(')', equalsIndex);
if (closingBracketIndex === -1 || coursePathIndex === -1 || faqIdIndex === -1 ||
equalsIndex === -1 || closingParenIndex === -1) {
console.warn('Malformed FAQ reference detected');
return;
}
referenceStr = this.content.substring(closingBracketIndex + 1, coursePathIndex);
linkToReference = [this.content.substring(coursePathIndex + 1, faqIdIndex)];
queryParams = { faqId: this.content.substring(equalsIndex + 1, closingParenIndex) } as Params;

Comment on lines +181 to +226
it('should have set the correct default commands on init if faq is enabled', () => {
jest.spyOn(CourseModel, 'isFaqEnabled').mockReturnValueOnce(true);
component.ngOnInit();

expect(component.defaultActions).toEqual(
expect.arrayContaining([
expect.any(BoldAction),
expect.any(ItalicAction),
expect.any(UnderlineAction),
expect.any(QuoteAction),
expect.any(CodeAction),
expect.any(CodeBlockAction),
expect.any(EmojiAction),
expect.any(UrlAction),
expect.any(AttachmentAction),
expect.any(UserMentionAction),
expect.any(ChannelReferenceAction),
expect.any(ExerciseReferenceAction),
expect.any(FaqReferenceAction),
]),
);

expect(component.lectureAttachmentReferenceAction).toEqual(new LectureAttachmentReferenceAction(metisService, lectureService));
});

it('should have set the correct default commands on init if faq is disabled', () => {
jest.spyOn(CourseModel, 'isFaqEnabled').mockReturnValueOnce(false);
component.ngOnInit();

expect(component.defaultActions).toEqual(
expect.arrayContaining([
expect.any(BoldAction),
expect.any(ItalicAction),
expect.any(UnderlineAction),
expect.any(QuoteAction),
expect.any(CodeAction),
expect.any(CodeBlockAction),
expect.any(EmojiAction),
expect.any(UrlAction),
expect.any(AttachmentAction),
expect.any(ExerciseReferenceAction),
]),
);

expect(component.lectureAttachmentReferenceAction).toEqual(new LectureAttachmentReferenceAction(metisService, lectureService));
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider reducing duplication and adding integration test.

  1. The expectation arrays in both tests contain significant duplication. Consider extracting common actions to a shared constant.
  2. Add a test to verify the actual integration with FaqReferenceAction.
// Extract common actions
const BASE_ACTIONS = [
    expect.any(BoldAction),
    expect.any(ItalicAction),
    expect.any(UnderlineAction),
    expect.any(QuoteAction),
    expect.any(CodeAction),
    expect.any(CodeBlockAction),
    expect.any(EmojiAction),
    expect.any(UrlAction),
    expect.any(AttachmentAction),
];

const COMMUNICATION_ACTIONS = [
    expect.any(UserMentionAction),
    expect.any(ChannelReferenceAction),
];

// Add integration test
it('should properly integrate FaqReferenceAction when enabled', () => {
    jest.spyOn(CourseModel, 'isFaqEnabled').mockReturnValueOnce(true);
    component.ngOnInit();
    
    const faqAction = component.defaultActions.find(action => action instanceof FaqReferenceAction);
    expect(faqAction).toBeTruthy();
    
    // Test the action's behavior
    const mockEditor = { /* ... */ };
    faqAction.run(mockEditor);
    // Add expectations for the action's behavior
});

@@ -92,11 +96,13 @@ describe('MonacoEditorCommunicationActionIntegration', () => {
{ actionId: ChannelReferenceAction.ID, defaultInsertText: '#', triggerCharacter: '#' },
{ actionId: UserMentionAction.ID, defaultInsertText: '@', triggerCharacter: '@' },
{ actionId: ExerciseReferenceAction.ID, defaultInsertText: '/exercise', triggerCharacter: '/' },
{ actionId: FaqReferenceAction.ID, defaultInsertText: '/faq', triggerCharacter: '/' },
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using a type alias for better maintainability.

The union type for the action variable is growing longer. Consider extracting it to a type alias.

type SupportedAction = ChannelReferenceAction | UserMentionAction | ExerciseReferenceAction | FaqReferenceAction;
let action: SupportedAction;

Also applies to: 101-101, 105-105, 115-115

Comment on lines +28 to +52
register(editor: TextEditor, translateService: TranslateService): void {
super.register(editor, translateService);
const faqs = this.metisService.getCourse().faqs ?? [];
this.setValues(
faqs.map((faq) => ({
id: faq.id!.toString(),
value: faq.questionTitle!,
type: 'faq',
})),
);

this.disposableCompletionProvider = this.registerCompletionProviderForCurrentModel<ValueItem>(
editor,
() => Promise.resolve(this.getValues()),
(item: ValueItem, range: TextEditorRange) =>
new TextEditorCompletionItem(
`/faq ${item.value}`,
item.type,
`[${item.type}]${item.value}(${this.metisService.getLinkForFaq()}?faqId=${item.id})[/${item.type}]`,
TextEditorCompletionItemKind.Default,
range,
),
'/',
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider updating FAQs dynamically to reflect changes.

Currently, the FAQs are fetched once during the registration of the action. If FAQs are added or modified after the editor initializes, these changes won't appear in the completion suggestions. To ensure users always see the most recent FAQs, consider implementing a mechanism to refresh the FAQ list when updates occur.

Comment on lines +44 to +46
`/faq ${item.value}`,
item.type,
`[${item.type}]${item.value}(${this.metisService.getLinkForFaq()}?faqId=${item.id})[/${item.type}]`,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Localize user-facing strings in completion items.

The strings used in the TextEditorCompletionItem constructor, such as /faq ${item.value} and [${item.type}]${item.value}(...)[/${item.type}], are displayed to the user. To adhere to localization guidelines (localize:true), please use the translateService to localize these user-facing texts.

@@ -1,6 +1,6 @@
import { Component, OnDestroy, OnInit, ViewEncapsulation, inject } from '@angular/core';
import { Component, ElementRef, OnDestroy, OnInit, ViewEncapsulation, effect, inject, viewChildren } from '@angular/core';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct the casing of ViewChildren in the import statement

The import statement incorrectly uses viewChildren instead of ViewChildren. In Angular, decorators and class names should follow PascalCase naming convention.

Apply this diff to fix the import:

-import { Component, ElementRef, OnDestroy, OnInit, ViewEncapsulation, effect, inject, viewChildren } from '@angular/core';
+import { Component, ElementRef, OnDestroy, OnInit, ViewEncapsulation, effect, inject, ViewChildren } from '@angular/core';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { Component, ElementRef, OnDestroy, OnInit, ViewEncapsulation, effect, inject, viewChildren } from '@angular/core';
import { Component, ElementRef, OnDestroy, OnInit, ViewEncapsulation, effect, inject, ViewChildren } from '@angular/core';

}

scrollToFaq(faqId: number): void {
const faqElement = this.faqElements().find((faq) => faq.nativeElement.id === 'faq-' + String(faqId));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Access faqElements correctly and remove parentheses

The faqElements is a property, not a method. Remove the parentheses when accessing it. Additionally, use toArray() to convert the QueryList to an array before using find.

Apply this diff to fix the usage:

-const faqElement = this.faqElements().find((faq) => faq.nativeElement.id === 'faq-' + String(faqId));
+const faqElement = this.faqElements.toArray().find((faq) => faq.nativeElement.id === 'faq-' + String(faqId));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const faqElement = this.faqElements().find((faq) => faq.nativeElement.id === 'faq-' + String(faqId));
const faqElement = this.faqElements.toArray().find((faq) => faq.nativeElement.id === 'faq-' + String(faqId));

@@ -27,10 +29,12 @@
imports: [ArtemisSharedComponentModule, ArtemisSharedModule, CourseFaqAccordionComponent, CustomExerciseCategoryBadgeComponent, SearchFilterComponent, ArtemisMarkdownModule],
})
export class CourseFaqComponent implements OnInit, OnDestroy {
faqElements = viewChildren<ElementRef>('faqElement');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Use the @ViewChildren decorator for faqElements

Currently, faqElements is assigned using viewChildren, but it should be declared using the @ViewChildren decorator to properly query child elements.

Apply this diff to correct the property declaration:

-faqElements = viewChildren<ElementRef>('faqElement');
+@ViewChildren('faqElement') faqElements: QueryList<ElementRef>;

Also, ensure you import QueryList from @angular/core:

+import { ..., QueryList } from '@angular/core';

Committable suggestion was skipped due to low confidence.

@cremertim cremertim closed this Oct 29, 2024
@cremertim cremertim deleted the feature/communication/add-answers-to-post-search branch October 29, 2024 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client Pull requests that update TypeScript code. (Added Automatically!) communication Pull requests that affect the corresponding module server Pull requests that update Java code. (Added Automatically!) tests
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

1 participant