From 5e0990e4a4789b4b207e271fe4930e8d82ea5d09 Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Mon, 23 Sep 2024 02:09:54 +0300 Subject: [PATCH 01/25] updated sidebar with new buttons, fix channel prefixes, fix empty section view --- .../course-conversations.component.html | 6 +- .../course-conversations.component.ts | 88 ++++++---- .../channels-overview-dialog.component.html | 32 +--- .../channels-overview-dialog.component.ts | 40 +---- .../conversation-settings.component.ts | 4 +- .../accordion-add-options.component.html | 3 - .../accordion-add-options.component.scss | 12 -- .../accordion-add-options.component.ts | 20 --- .../sidebar-accordion.component.html | 80 +++++---- .../sidebar-accordion.component.ts | 11 +- .../sidebar-card-item.component.html | 2 +- .../sidebar-card-item.component.ts | 12 ++ .../sidebar-card-large.component.html | 2 +- .../sidebar-card-large.component.ts | 1 + .../sidebar-card-medium.component.html | 2 +- .../sidebar-card-medium.component.ts | 1 + .../sidebar-card-small.component.html | 2 +- .../sidebar-card-small.component.ts | 1 + .../shared/sidebar/sidebar-card.directive.ts | 2 + .../shared/sidebar/sidebar-event.service.ts | 10 -- .../app/shared/sidebar/sidebar.component.html | 31 +++- .../app/shared/sidebar/sidebar.component.scss | 62 +++++++ .../app/shared/sidebar/sidebar.component.ts | 55 ++++-- .../app/shared/sidebar/sidebar.module.ts | 4 +- src/main/webapp/app/types/sidebar.ts | 1 - src/main/webapp/i18n/de/conversation.json | 7 +- .../webapp/i18n/de/student-dashboard.json | 4 + src/main/webapp/i18n/en/conversation.json | 7 +- .../webapp/i18n/en/student-dashboard.json | 4 + .../course-conversations.component.spec.ts | 159 ++++++------------ .../accordion-add-options.component.spec.ts | 45 ----- .../sidebar-accordion.component.spec.ts | 4 +- 32 files changed, 322 insertions(+), 392 deletions(-) delete mode 100644 src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.html delete mode 100644 src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.scss delete mode 100644 src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.ts delete mode 100644 src/test/javascript/spec/component/shared/sidebar/accordion-add-options.component.spec.ts 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 9979347728ec..0919ce1fa93b 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 @@ -29,10 +29,14 @@ [itemSelected]="conversationSelected" [courseId]="course.id" [sidebarData]="sidebarData" - (onPlusPressed)="onAccordionPlusButtonPressed($event)" + (onCreateChannelPressed)="onCreateChannelPressed()" + (onBrowsePressed)="onBrowseChannelPressed()" + (onDirectChatPressed)="onCreateDirectChatPressed()" + (onGroupChatPressed)="onCreateGroupChatPressed()" [showAddOption]="CHANNEL_TYPE_SHOW_ADD_OPTION" [channelTypeIcon]="CHANNEL_TYPE_ICON" [collapseState]="DEFAULT_COLLAPSE_STATE" + [inCommunication]="true" /> @if (course && !activeConversation && isCodeOfConductPresented) { 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 159818511aa8..6b950c2c7602 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 @@ -3,10 +3,10 @@ import { ConversationDTO } from 'app/entities/metis/conversation/conversation.mo import { Post } from 'app/entities/metis/post.model'; import { ActivatedRoute, Router } from '@angular/router'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; -import { EMPTY, Subject, Subscription, from, take, takeUntil } from 'rxjs'; -import { catchError } from 'rxjs/operators'; +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 { ChannelSubType, getAsChannelDTO } from 'app/entities/metis/conversation/channel.model'; +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'; @@ -16,11 +16,12 @@ import { CourseWideSearchComponent, CourseWideSearchConfig } from 'app/overview/ import { AccordionGroups, ChannelAccordionShowAdd, ChannelTypeIcons, CollapseState, SidebarCardElement, SidebarData } from 'app/types/sidebar'; import { CourseOverviewService } from 'app/overview/course-overview.service'; import { GroupChatCreateDialogComponent } from 'app/overview/course-conversations/dialogs/group-chat-create-dialog/group-chat-create-dialog.component'; -import { defaultFirstLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util'; +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 { ChannelsOverviewDialogComponent } from 'app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-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'; const DEFAULT_CHANNEL_GROUPS: AccordionGroups = { favoriteChannels: { entityData: [] }, @@ -114,7 +115,9 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { faFilter = faFilter; faSearch = faSearch; - // MetisConversationService is created in course overview, so we can use it here + private createChannelFn: (channel: ChannelDTO) => Observable; + channelActions$ = new Subject(); + constructor( private router: Router, private activatedRoute: ActivatedRoute, @@ -162,6 +165,10 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { this.metisConversationService.checkIsCodeOfConductAccepted(this.course!); this.isServiceSetUp = true; } + this.channelActions$.pipe(debounceTime(500), distinctUntilChanged(), takeUntil(this.ngUnsubscribe)).subscribe((channelAction) => { + this.performChannelAction(channelAction); + }); + this.createChannelFn = (channel: ChannelDTO) => this.metisConversationService.createChannel(channel); }); this.profileSubscription = this.profileService.getProfileInfo()?.subscribe((profileInfo) => { @@ -170,6 +177,18 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { }); } + performChannelAction(channelAction: ChannelAction) { + if (this.createChannelFn) { + this.createChannelFn(channelAction.channel) + .pipe(takeUntil(this.ngUnsubscribe)) + .subscribe({ + complete: () => { + this.prepareSidebarData(); + }, + }); + } + } + subscribeToQueryParameter() { this.activatedRoute.queryParams.pipe(take(1), takeUntil(this.ngUnsubscribe)).subscribe((queryParams) => { if (queryParams.conversationId) { @@ -276,7 +295,6 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { storageId: 'conversation', groupedData: this.accordionConversationGroups, ungroupedData: this.sidebarConversations, - showAccordionAddOption: true, showAccordionLeadingIcon: true, }; } @@ -290,14 +308,20 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { this.courseOverviewService.setSidebarCollapseState('conversation', this.isCollapsed); } - onAccordionPlusButtonPressed(chatType: string) { - if (chatType === 'groupChats') { - this.openCreateGroupChatDialog(); - } else if (chatType === 'directMessages') { - this.openCreateOneToOneChatDialog(); - } else { - this.openChannelOverviewDialog(chatType); - } + onCreateChannelPressed() { + this.openCreateChannelDialog(); + } + + onCreateGroupChatPressed() { + this.openCreateGroupChatDialog(); + } + + onCreateDirectChatPressed() { + this.openCreateOneToOneChatDialog(); + } + + onBrowseChannelPressed() { + this.openChannelOverviewDialog(); } openCreateGroupChatDialog() { @@ -338,8 +362,22 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { }); } - openChannelOverviewDialog(groupKey: string) { - const subType = this.getChannelSubType(groupKey); + openCreateChannelDialog() { + const modalRef: NgbModalRef = this.modalService.open(ChannelsCreateDialogComponent, defaultSecondLayerDialogOptions); + modalRef.componentInstance.course = this.course; + modalRef.componentInstance.initialize(); + from(modalRef.result) + .pipe( + catchError(() => EMPTY), + takeUntil(this.ngUnsubscribe), + ) + .subscribe((channel: ChannelDTO) => { + this.channelActions$.next({ action: 'create', channel }); + }); + } + + openChannelOverviewDialog() { + const subType = null; const modalRef: NgbModalRef = this.modalService.open(ChannelsOverviewDialogComponent, defaultFirstLayerDialogOptions); modalRef.componentInstance.course = this.course; modalRef.componentInstance.createChannelFn = subType === ChannelSubType.GENERAL ? this.metisConversationService.createChannel : undefined; @@ -369,22 +407,6 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { }); } - getChannelSubType(groupKey: string) { - if (groupKey === 'exerciseChannels') { - return ChannelSubType.EXERCISE; - } - if (groupKey === 'generalChannels') { - return ChannelSubType.GENERAL; - } - if (groupKey === 'lectureChannels') { - return ChannelSubType.LECTURE; - } - if (groupKey === 'examChannels') { - return ChannelSubType.EXAM; - } - return ChannelSubType.GENERAL; - } - toggleChannelSearch() { this.channelSearchCollapsed = !this.channelSearchCollapsed; } diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.html b/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.html index b19e105f22cd..da14d6d48287 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.html +++ b/src/main/webapp/app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.html @@ -3,7 +3,7 @@
@@ -22,36 +22,6 @@
} - @if (otherChannels && otherChannels.length > 0) { -
-
- - -
-
-
-
    - @for (channel of otherChannels; track otherChannels.indexOf(channel)) { -
  • - -
  • - } -
-
-
-
- } - - } 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 046f49428f99..dc828e521dbc 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,19 +1,16 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; -import { EMPTY, Observable, Subject, debounceTime, distinctUntilChanged, finalize, from, map, takeUntil } from 'rxjs'; +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, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +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 { ChannelsCreateDialogComponent } from 'app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component'; import { canCreateChannel } from 'app/shared/metis/conversations/conversation-permissions.utils'; import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; -import { defaultSecondLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util'; -import { catchError } from 'rxjs/operators'; export type ChannelActionType = 'register' | 'deregister' | 'view' | 'create'; export type ChannelAction = { @@ -44,12 +41,10 @@ export class ChannelsOverviewDialogComponent extends AbstractDialogComponent imp channelModificationPerformed = false; isLoading = false; channels: ChannelDTO[] = []; - otherChannels: ChannelDTO[] = []; isInitialized = false; faChevronRight = faChevronRight; - otherChannelsAreCollapsed = true; initialize() { super.initialize(['course', 'channelSubType']); @@ -61,7 +56,6 @@ export class ChannelsOverviewDialogComponent extends AbstractDialogComponent imp constructor( private channelService: ChannelService, private alertService: AlertService, - private modalService: NgbModal, activeModal: NgbActiveModal, ) { @@ -114,18 +108,6 @@ export class ChannelsOverviewDialogComponent extends AbstractDialogComponent imp case 'view': this.close([channelAction.channel, this.channelModificationPerformed]); break; - case 'create': - if (this.createChannelFn) { - this.createChannelFn(channelAction.channel) - .pipe(takeUntil(this.ngUnsubscribe)) - .subscribe({ - complete: () => { - this.loadChannelsOfCourse(); - this.channelModificationPerformed = true; - }, - }); - } - break; } } @@ -142,8 +124,7 @@ export class ChannelsOverviewDialogComponent extends AbstractDialogComponent imp ) .subscribe({ next: (channels: ChannelDTO[]) => { - this.channels = channels?.filter((channel) => channel.subType === this.channelSubType) ?? []; - this.otherChannels = channels?.filter((channel) => channel.subType !== this.channelSubType) ?? []; + this.channels = channels; this.noOfChannels = this.channels.length; }, error: (errorResponse: HttpErrorResponse) => { @@ -151,19 +132,4 @@ export class ChannelsOverviewDialogComponent extends AbstractDialogComponent imp }, }); } - - openCreateChannelDialog(event: MouseEvent) { - event.stopPropagation(); - const modalRef: NgbModalRef = this.modalService.open(ChannelsCreateDialogComponent, defaultSecondLayerDialogOptions); - modalRef.componentInstance.course = this.course; - modalRef.componentInstance.initialize(); - from(modalRef.result) - .pipe( - catchError(() => EMPTY), - takeUntil(this.ngUnsubscribe), - ) - .subscribe((channel: ChannelDTO) => { - this.channelActions$.next({ action: 'create', channel }); - }); - } } 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 64cb3410ae78..73013a8e76df 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 @@ -9,7 +9,7 @@ import { onError } from 'app/shared/util/global.utils'; import { EMPTY, Subject, from, takeUntil } from 'rxjs'; import { HttpErrorResponse } from '@angular/common/http'; import { AlertService } from 'app/core/util/alert.service'; -import { faTimes } from '@fortawesome/free-solid-svg-icons'; +import { faTrash } from '@fortawesome/free-solid-svg-icons'; import { canChangeChannelArchivalState, canDeleteChannel, canLeaveConversation } from 'app/shared/metis/conversations/conversation-permissions.utils'; import { GroupChatService } from 'app/shared/metis/conversations/group-chat.service'; import { isGroupChatDTO } from 'app/entities/metis/conversation/group-chat.model'; @@ -42,7 +42,7 @@ export class ConversationSettingsComponent implements OnInit, OnDestroy { private dialogErrorSource = new Subject(); dialogError$ = this.dialogErrorSource.asObservable(); - faTimes = faTimes; + readonly faTimes = faTrash; conversationAsChannel: ChannelDTO | undefined; canLeaveConversation: boolean; diff --git a/src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.html b/src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.html deleted file mode 100644 index 5b240eab45c3..000000000000 --- a/src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.scss b/src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.scss deleted file mode 100644 index a9acacbbb52e..000000000000 --- a/src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.scss +++ /dev/null @@ -1,12 +0,0 @@ -.dropdown-toggle::after { - display: none; -} - -.sidebar-button { - border: 0; - - &:hover { - background-color: var(--transparent); - color: var(--link-item-color); - } -} diff --git a/src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.ts b/src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.ts deleted file mode 100644 index f8001221c8b6..000000000000 --- a/src/main/webapp/app/shared/sidebar/accordion-add-options/accordion-add-options.component.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { faPlus } from '@fortawesome/free-solid-svg-icons'; -import { SidebarEventService } from 'app/shared/sidebar/sidebar-event.service'; - -@Component({ - selector: 'jhi-accordion-add-options', - templateUrl: './accordion-add-options.component.html', - styleUrl: './accordion-add-options.component.scss', -}) -export class AccordionAddOptionsComponent { - @Input() groupKey: string; - faPlus = faPlus; - - constructor(private sidebarEventService: SidebarEventService) {} - - onPlusPressed(event: MouseEvent) { - event.stopPropagation(); - this.sidebarEventService.emitSidebarAccordionPlusClickedEvent(this.groupKey); - } -} diff --git a/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html b/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html index bb8af797054e..16eb4bc62671 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html @@ -1,46 +1,44 @@ @for (groupKey of Object.keys(groupedData); track groupKey; let i = $index) { -
-
-
- @if (showLeadingIcon) { - - } - {{ 'artemisApp.courseOverview.sidebar.' + groupKey | artemisTranslate | titlecase }} - ({{ (groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue)?.length }}) -
-
- - + @if ((groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue)?.length) { +
+
+
+ @if (showLeadingIcon) { + + } + {{ 'artemisApp.courseOverview.sidebar.' + groupKey | artemisTranslate | titlecase }} + ({{ (groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue)?.length }}) +
+
+ +
-
- @if ((groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue)?.length) { -
-
- @for (sidebarItem of groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue; let last = $last; track sidebarItem) { -
- -
-
- } -
- @if (!collapseState[groupKey]) { + @if ((groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue)?.length) {
+
+ @for (sidebarItem of groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue; let last = $last; track sidebarItem) { +
+ +
+
+ } +
+ @if (!collapseState[groupKey]) { +
+ } } - } -
+
+ } } diff --git a/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.ts b/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.ts index 5cd59c0d3b8e..9f185af2da2d 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.ts @@ -1,6 +1,6 @@ import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'; import { faChevronRight, faFile } from '@fortawesome/free-solid-svg-icons'; -import { AccordionGroups, ChannelAccordionShowAdd, ChannelGroupCategory, ChannelTypeIcons, CollapseState, SidebarCardElement, SidebarTypes } from 'app/types/sidebar'; +import { AccordionGroups, ChannelTypeIcons, CollapseState, SidebarCardElement, SidebarTypes } from 'app/types/sidebar'; import { Params } from '@angular/router'; @Component({ @@ -20,8 +20,6 @@ export class SidebarAccordionComponent implements OnChanges, OnInit { @Input() courseId?: number; @Input() itemSelected?: boolean; @Input() showLeadingIcon = false; - @Input() showAddOptions = false; - @Input() showAddOption?: ChannelAccordionShowAdd; @Input() channelTypeIcon?: ChannelTypeIcons; @Input() collapseState: CollapseState; @Input() isFilterActive: boolean = false; @@ -72,11 +70,4 @@ export class SidebarAccordionComponent implements OnChanges, OnInit { this.collapseState[groupCategoryKey] = !this.collapseState[groupCategoryKey]; sessionStorage.setItem('sidebar.accordion.collapseState.' + this.storageId + '.byCourse.' + this.courseId, JSON.stringify(this.collapseState)); } - - getGroupKey(groupKey: string): boolean { - if (!this.showAddOption) { - return false; - } - return this.showAddOption[groupKey as ChannelGroupCategory]; - } } diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.html b/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.html index ce2055d4c657..519baa631d2d 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.html @@ -51,7 +51,7 @@ @if (sidebarItem.icon) { } - {{ sidebarItem.title }} @if (sidebarItem.rightIcon) { diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts b/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts index 0db256223fe5..b8d336fe8fa7 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts @@ -9,4 +9,16 @@ import { SidebarCardElement, SidebarTypes } from 'app/types/sidebar'; export class SidebarCardItemComponent { @Input() sidebarItem: SidebarCardElement; @Input() sidebarType?: SidebarTypes; + @Input() groupKey?: string; + + removeChannelPrefix(item: SidebarCardElement, name: string): string { + const prefixes = ['exercise-', 'lecture-', 'exam-']; + const channelTypes = ['exerciseChannels', 'lectureChannels', 'examChannels']; + for (const prefix of prefixes) { + if (name.startsWith(prefix) && channelTypes.includes(this.groupKey)) { + return name.substring(prefix.length); + } + } + return name; + } } diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.html b/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.html index 6f5f92481893..722876bb9e29 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.html @@ -5,5 +5,5 @@ (click)="emitStoreAndRefresh(sidebarItem.id)" [routerLinkActive]="'bg-selected border-selected'" > - +
diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.ts b/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.ts index 40afd9615c37..01194afbc0cd 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.ts @@ -13,6 +13,7 @@ export class SidebarCardLargeComponent { @Input({ required: true }) sidebarItem: SidebarCardElement; @Input() sidebarType?: SidebarTypes; @Input() itemSelected?: boolean; + @Input() groupKey?: string; constructor( private sidebarEventService: SidebarEventService, private router: Router, diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.html b/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.html index adfa1da77f82..6307e36257bc 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.html @@ -5,7 +5,7 @@ [ngClass]="{ 'bg-selected border-selected': itemSelected }" (click)="emitPageChangeForExam()" > - + } @else {
(); + @Input() groupKey?: string; constructor( private sidebarEventService: SidebarEventService, diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.html b/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.html index 4ba959ab59fc..59c761f4c58d 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.html @@ -10,7 +10,7 @@ [routerLinkActive]="'bg-selected border-selected'" >
- +
@if (sidebarItem.conversation) {
diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.ts b/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.ts index 68dc7bbb75f5..9f44d84aad26 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.ts @@ -16,6 +16,7 @@ export class SidebarCardSmallComponent { @Input({ required: true }) sidebarItem: SidebarCardElement; @Input() sidebarType?: SidebarTypes; @Input() itemSelected?: boolean; + @Input() groupKey?: string; constructor( private sidebarEventService: SidebarEventService, diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card.directive.ts b/src/main/webapp/app/shared/sidebar/sidebar-card.directive.ts index d197f711bc6b..4450b7bda49d 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card.directive.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-card.directive.ts @@ -13,6 +13,7 @@ export class SidebarCardDirective implements OnInit, OnDestroy { @Input() sidebarItem: SidebarCardElement; @Input() sidebarType?: SidebarTypes; @Input() itemSelected?: boolean; + @Input() groupKey?: string; @Output() onUpdateSidebar = new EventEmitter(); @@ -45,6 +46,7 @@ export class SidebarCardDirective implements OnInit, OnDestroy { this.componentRef.instance.itemSelected = this.itemSelected; this.componentRef.instance.sidebarType = this.sidebarType; this.componentRef.instance.sidebarItem = this.sidebarItem; + this.componentRef.instance.groupKey = this.groupKey; if (this.size == 'S') { this.componentRef.instance.onUpdateSidebar = this.onUpdateSidebar; } diff --git a/src/main/webapp/app/shared/sidebar/sidebar-event.service.ts b/src/main/webapp/app/shared/sidebar/sidebar-event.service.ts index b383a1847516..2db0f5fabfc4 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-event.service.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-event.service.ts @@ -4,7 +4,6 @@ import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class SidebarEventService { private sidebarCardClickedEvent = new BehaviorSubject(null); - private sidebarAccordionPlusClickedEvent = new BehaviorSubject(null); emitSidebarCardEvent(itemId: string | number) { this.sidebarCardClickedEvent.next(itemId); @@ -17,13 +16,4 @@ export class SidebarEventService { sidebarCardEventListener() { return this.sidebarCardClickedEvent.asObservable(); } - - emitSidebarAccordionPlusClickedEvent(groupkey: string) { - this.sidebarAccordionPlusClickedEvent.next(groupkey); - this.sidebarAccordionPlusClickedEvent.next(null); - } - - sidebarAccordionPlusClickedEventListener() { - return this.sidebarAccordionPlusClickedEvent.asObservable(); - } } diff --git a/src/main/webapp/app/shared/sidebar/sidebar.component.html b/src/main/webapp/app/shared/sidebar/sidebar.component.html index ea513b48174e..eeadeb3896fa 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar.component.html @@ -18,6 +18,35 @@ }
} + @if (inCommunication) { +
+
+ +
+
+ +
+
+ } + @if (!sidebarData?.ungroupedData || !(sidebarData?.ungroupedData | searchFilter: ['title', 'type'] : searchValue)?.length) {
(); @Output() onUpdateSidebar = new EventEmitter(); - @Output() onPlusPressed = new EventEmitter(); + @Output() onDirectChatPressed = new EventEmitter(); + @Output() onGroupChatPressed = new EventEmitter(); + @Output() onBrowsePressed = new EventEmitter(); + @Output() onCreateChannelPressed = new EventEmitter(); @Input() searchFieldEnabled: boolean = true; @Input() sidebarData: SidebarData; @Input() courseId?: number; @@ -33,6 +36,7 @@ export class SidebarComponent implements OnDestroy, OnChanges, OnInit { @Input() channelTypeIcon?: ChannelTypeIcons; @Input() collapseState: CollapseState; @Input() showFilter: boolean = false; + @Input() inCommunication: boolean = false; searchValue = ''; isCollapsed: boolean = false; @@ -42,7 +46,6 @@ export class SidebarComponent implements OnDestroy, OnChanges, OnInit { paramSubscription?: Subscription; profileSubscription?: Subscription; sidebarEventSubscription?: Subscription; - sidebarAccordionEventSubscription?: Subscription; routeParams: Params; isProduction = true; @@ -65,6 +68,43 @@ export class SidebarComponent implements OnDestroy, OnChanges, OnInit { private modalService: NgbModal, ) {} + showChatDropdown = false; + showChannelDropdown = false; + + toggleChatDropdown() { + this.showChatDropdown = !this.showChatDropdown; + if (this.showChatDropdown) { + this.showChannelDropdown = false; + } + } + + toggleChannelDropdown() { + this.showChannelDropdown = !this.showChannelDropdown; + if (this.showChannelDropdown) { + this.showChatDropdown = false; + } + } + + createNewChannel() { + this.onCreateChannelPressed.emit(); + this.showChannelDropdown = false; + } + + browseChannels() { + this.onBrowsePressed.emit(); + this.showChannelDropdown = false; + } + + createDirectChat() { + this.onDirectChatPressed.emit(); + this.showChatDropdown = false; + } + + createGroupChat() { + this.onGroupChatPressed.emit(); + this.showChatDropdown = false; + } + ngOnInit(): void { this.profileSubscription = this.profileService.getProfileInfo()?.subscribe((profileInfo) => { this.isProduction = profileInfo?.inProduction; @@ -84,17 +124,6 @@ export class SidebarComponent implements OnDestroy, OnChanges, OnInit { } } }); - - this.sidebarAccordionEventSubscription = this.sidebarEventService - .sidebarAccordionPlusClickedEventListener() - .pipe( - distinctUntilChanged(), // This ensures the function is only called when the actual value changes - ) - .subscribe((groupKey) => { - if (groupKey) { - this.onPlusPressed.emit(groupKey); - } - }); } ngOnChanges() { diff --git a/src/main/webapp/app/shared/sidebar/sidebar.module.ts b/src/main/webapp/app/shared/sidebar/sidebar.module.ts index cc34473eb48f..4adef3f1c964 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar.module.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar.module.ts @@ -14,7 +14,6 @@ import { ArtemisSharedCommonModule } from '../shared-common.module'; import { SubmissionResultStatusModule } from 'app/overview/submission-result-status.module'; import { SidebarCardDirective } from 'app/shared/sidebar/sidebar-card.directive'; import { ConversationOptionsComponent } from 'app/shared/sidebar/conversation-options/conversation-options.component'; -import { AccordionAddOptionsComponent } from 'app/shared/sidebar/accordion-add-options/accordion-add-options.component'; import { ArtemisExamSharedModule } from 'app/exam/shared/exam-shared.module'; @NgModule({ @@ -37,8 +36,7 @@ import { ArtemisExamSharedModule } from 'app/exam/shared/exam-shared.module'; SidebarCardItemComponent, SidebarComponent, ConversationOptionsComponent, - AccordionAddOptionsComponent, ], - exports: [SidebarComponent, SidebarCardSmallComponent, SidebarCardMediumComponent, SidebarCardLargeComponent, ConversationOptionsComponent, AccordionAddOptionsComponent], + exports: [SidebarComponent, SidebarCardSmallComponent, SidebarCardMediumComponent, SidebarCardLargeComponent, ConversationOptionsComponent], }) export class ArtemisSidebarModule {} diff --git a/src/main/webapp/app/types/sidebar.ts b/src/main/webapp/app/types/sidebar.ts index cdaae8d3b99f..f97edd88f880 100644 --- a/src/main/webapp/app/types/sidebar.ts +++ b/src/main/webapp/app/types/sidebar.ts @@ -32,7 +32,6 @@ export interface SidebarData { groupedData?: AccordionGroups; ungroupedData?: SidebarCardElement[]; storageId?: string; - showAccordionAddOption?: boolean; showAccordionLeadingIcon?: boolean; } diff --git a/src/main/webapp/i18n/de/conversation.json b/src/main/webapp/i18n/de/conversation.json index ce02d61019c9..2f1048edface 100644 --- a/src/main/webapp/i18n/de/conversation.json +++ b/src/main/webapp/i18n/de/conversation.json @@ -192,12 +192,7 @@ } }, "channelOverview": { - "title": { - "general": "Alle allgemeinen Kanäle im Kurs {{ courseTitle }}", - "exercise": "Alle Aufgabenkanäle im Kurs {{ courseTitle }}", - "lecture": "Alle Vorlesungskanäle im Kurs {{ courseTitle }}", - "exam": "Alle Klausurkanäle im Kurs {{ courseTitle }}" - }, + "title": "Alle Kanäle im Kurs {{ courseTitle }}", "otherChannels": "Andere Kanäle", "createChannelButton": "Erstelle einen neuen Kanal", "channelItem": { diff --git a/src/main/webapp/i18n/de/student-dashboard.json b/src/main/webapp/i18n/de/student-dashboard.json index 89729023ff10..2f8ee69fa76a 100644 --- a/src/main/webapp/i18n/de/student-dashboard.json +++ b/src/main/webapp/i18n/de/student-dashboard.json @@ -63,8 +63,12 @@ "exerciseChannels": "Aufgaben", "lectureChannels": "Vorlesungen", "examChannels": "Klausuren", + "addChannel": "Kanal hinzufügen", "createChannel": "Kanal erstellen", "browseChannels": "Kanäle durchsuchen", + "createChat": "Chat erstellen", + "createGroupChat": "Gruppen-Chat erstellen", + "createDirectChat": "Direkt-Chat erstellen", "groupChats": "Gruppen-Chats", "directMessages": "Direktnachrichten", "filterConversationPlaceholder": "Konversationen filtern" diff --git a/src/main/webapp/i18n/en/conversation.json b/src/main/webapp/i18n/en/conversation.json index 40cb47bb8f98..af0b5b9cb1f2 100644 --- a/src/main/webapp/i18n/en/conversation.json +++ b/src/main/webapp/i18n/en/conversation.json @@ -191,12 +191,7 @@ } }, "channelOverview": { - "title": { - "general": "All General Channels in course {{ courseTitle }}", - "exercise": "All Exercise Channels in course {{ courseTitle }}", - "lecture": "All Lecture Channels in course {{ courseTitle }}", - "exam": "All Exam Channels in course {{ courseTitle }}" - }, + "title": "All Channels in course {{ courseTitle }}", "otherChannels": "Other Channels", "createChannelButton": "Create a new channel", "channelItem": { diff --git a/src/main/webapp/i18n/en/student-dashboard.json b/src/main/webapp/i18n/en/student-dashboard.json index ae6d54800cd6..169a3ba97303 100644 --- a/src/main/webapp/i18n/en/student-dashboard.json +++ b/src/main/webapp/i18n/en/student-dashboard.json @@ -63,8 +63,12 @@ "exerciseChannels": "Exercises", "lectureChannels": "Lectures", "examChannels": "Exams", + "addChannel": "Add Channel", "createChannel": "Create channel", "browseChannels": "Browse channels", + "createChat": "Create Chat", + "createGroupChat": "Create group chat", + "createDirectChat": "Create direct chat", "groupChats": "Group Chats", "directMessages": "Direct Messages", "filterConversationPlaceholder": "Filter conversations" diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts index c67649159a60..4b52a1ae6bb9 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts @@ -1,6 +1,5 @@ import { CourseConversationsComponent } from 'app/overview/course-conversations/course-conversations.component'; import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; -import { Type } from '@angular/core'; import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model'; import { generateExampleChannelDTO, generateExampleGroupChatDTO, generateOneToOneChatDTO } from './helpers/conversationExampleModels'; import { MockComponent, MockPipe, MockProvider } from 'ng-mocks'; @@ -29,15 +28,10 @@ import { DocumentationButtonComponent } from 'app/shared/components/documentatio import { getElement } from '../../../helpers/utils/general.utils'; import { SidebarComponent } from 'app/shared/sidebar/sidebar.component'; import { CourseOverviewService } from 'app/overview/course-overview.service'; -import { ChannelSubType } from 'app/entities/metis/conversation/channel.model'; -import { UserPublicInfoDTO } from 'app/core/user/user.model'; import { GroupChatCreateDialogComponent } from 'app/overview/course-conversations/dialogs/group-chat-create-dialog/group-chat-create-dialog.component'; -import { AbstractDialogComponent } from 'app/overview/course-conversations/dialogs/abstract-dialog.component'; -import { defaultFirstLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util'; import { NgbCollapseMocksModule } from '../../../helpers/mocks/directive/ngbCollapseMocks.module'; import { NgbTooltipMocksModule } from '../../../helpers/mocks/directive/ngbTooltipMocks.module'; import { SidebarEventService } from 'app/shared/sidebar/sidebar-event.service'; -import { AccordionAddOptionsComponent } from 'app/shared/sidebar/accordion-add-options/accordion-add-options.component'; import { SidebarAccordionComponent } from 'app/shared/sidebar/sidebar-accordion/sidebar-accordion.component'; import { GroupChatDTO } from 'app/entities/metis/conversation/group-chat.model'; import { OneToOneChatCreateDialogComponent } from 'app/overview/course-conversations/dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component'; @@ -56,8 +50,8 @@ examples.forEach((activeConversation) => { let postsSubject: BehaviorSubject; let acceptCodeOfConductSpy: jest.SpyInstance; let setActiveConversationSpy: jest.SpyInstance; - let sidebarEventService: SidebarEventService; let metisConversationService: MetisConversationService; + let modalService: NgbModal; beforeEach(waitForAsync(() => { queryParamsSubject = new BehaviorSubject(convertToParamMap({})); @@ -73,7 +67,6 @@ examples.forEach((activeConversation) => { MockComponent(ButtonComponent), MockComponent(SidebarComponent), MockComponent(CourseWideSearchComponent), - MockComponent(AccordionAddOptionsComponent), MockComponent(SidebarAccordionComponent), MockPipe(ArtemisTranslatePipe), MockPipe(HtmlForMarkdownPipe), @@ -112,7 +105,6 @@ examples.forEach((activeConversation) => { }, }); - sidebarEventService = TestBed.inject(SidebarEventService); metisConversationService = TestBed.inject(MetisConversationService); Object.defineProperty(metisConversationService, 'isServiceSetup$', { get: () => new BehaviorSubject(true).asObservable() }); @@ -135,12 +127,10 @@ examples.forEach((activeConversation) => { postsSubject = new BehaviorSubject([]); jest.spyOn(metisConversationService, 'course', 'get').mockReturnValue(course); jest.spyOn(metisConversationService, 'activeConversation$', 'get').mockReturnValue(new BehaviorSubject(activeConversation).asObservable()); - jest.spyOn(sidebarEventService, 'emitSidebarAccordionPlusClickedEvent').mockImplementation((groupKey: string) => { - component.onAccordionPlusButtonPressed(groupKey); - }); setActiveConversationSpy = jest.spyOn(metisConversationService, 'setActiveConversation'); acceptCodeOfConductSpy = jest.spyOn(metisConversationService, 'acceptCodeOfConduct'); jest.spyOn(metisService, 'posts', 'get').mockReturnValue(postsSubject.asObservable()); + modalService = TestBed.inject(NgbModal); })); afterEach(() => { @@ -155,6 +145,54 @@ examples.forEach((activeConversation) => { expect(component.activeConversation).toEqual(activeConversation); }); + describe('Dialog Opening', () => { + it('should open the group chat creation dialog', fakeAsync(() => { + const mockModalRef: Partial = { + componentInstance: { + course: {}, + initialize: jest.fn(), + }, + result: Promise.resolve(undefined), + }; + const spy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as NgbModalRef); + component.onCreateGroupChatPressed(); + tick(); + expect(spy).toHaveBeenCalledWith(GroupChatCreateDialogComponent, expect.anything()); + })); + + it('should open the one-to-one chat creation dialog', fakeAsync(() => { + const mockModalRef: Partial = { + componentInstance: { + course: {}, + initialize: jest.fn(), + }, + result: Promise.resolve(undefined), + }; + const spy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as NgbModalRef); + component.onCreateDirectChatPressed(); + tick(); + expect(spy).toHaveBeenCalledWith(OneToOneChatCreateDialogComponent, expect.anything()); + })); + + it('should open the channel overview dialog', fakeAsync(() => { + const expectedResults = [undefined, true]; + const mockModalRef: Partial = { + componentInstance: { + course: {}, + createChannelFn: jest.fn(), + initialize: jest.fn(), + channelSubType: undefined, + }, + result: Promise.resolve(expectedResults), + }; + const spy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as NgbModalRef); + component.onBrowseChannelPressed(); + tick(); + expect(spy).toHaveBeenCalledWith(ChannelsOverviewDialogComponent, expect.anything()); + expect(mockModalRef.componentInstance.initialize).toHaveBeenCalled(); + })); + }); + it('should update thread in post', fakeAsync(() => { fixture.detectChanges(); component.postInThread = { id: 1, content: 'loremIpsum' } as Post; @@ -244,102 +282,5 @@ examples.forEach((activeConversation) => { component.onConversationSelected(activeConversation?.id ?? 1); expect(setActiveConversationSpy).toHaveBeenCalled(); }); - - it('getChannelSubType method should return correct SubType', () => { - expect(component.getChannelSubType('exerciseChannels')).toEqual(ChannelSubType.EXERCISE); - expect(component.getChannelSubType('examChannels')).toEqual(ChannelSubType.EXAM); - expect(component.getChannelSubType('generalChannels')).toEqual(ChannelSubType.GENERAL); - expect(component.getChannelSubType('lectureChannels')).toEqual(ChannelSubType.LECTURE); - }); - - it('onAccordionPlusButtonPressed method should call openCreateGroupChatDialog when groupKey is groupChats', () => { - const createGroupChatSpy = jest.spyOn(component, 'openCreateGroupChatDialog').mockImplementation(); - component.onAccordionPlusButtonPressed('groupChats'); - expect(createGroupChatSpy).toHaveBeenCalled(); - }); - - it('onAccordionPlusButtonPressed method should call openCreateOneToOneChatDialog when groupKey is directMessages', () => { - const createDirectMessageSpy = jest.spyOn(component, 'openCreateOneToOneChatDialog').mockImplementation(); - component.onAccordionPlusButtonPressed('directMessages'); - expect(createDirectMessageSpy).toHaveBeenCalled(); - }); - - it('onAccordionPlusButtonPressed method should call openChannelOverviewDialog when groupKey is any channelType', () => { - const openChannelOverviewDialogSpy = jest.spyOn(component, 'openChannelOverviewDialog').mockImplementation(); - component.onAccordionPlusButtonPressed('generalChannels'); - expect(openChannelOverviewDialogSpy).toHaveBeenCalledWith('generalChannels'); - component.onAccordionPlusButtonPressed('examChannels'); - expect(openChannelOverviewDialogSpy).toHaveBeenCalledWith('examChannels'); - component.onAccordionPlusButtonPressed('lectureChannels'); - expect(openChannelOverviewDialogSpy).toHaveBeenCalledWith('lectureChannels'); - component.onAccordionPlusButtonPressed('exerciseChannels'); - expect(openChannelOverviewDialogSpy).toHaveBeenCalledWith('exerciseChannels'); - component.onAccordionPlusButtonPressed('randomInputShouldAddedToGeneralChannels'); - expect(openChannelOverviewDialogSpy).toHaveBeenCalledWith('generalChannels'); - }); - - it('should open create group chat dialog when button is pressed', fakeAsync(() => { - const createGroupChatSpy = jest.spyOn(metisConversationService, 'createGroupChat').mockReturnValue(EMPTY); - createConversationDialogTest([new UserPublicInfoDTO()], GroupChatCreateDialogComponent, 'groupChats'); - fixture.whenStable().then(() => { - expect(createGroupChatSpy).toHaveBeenCalledOnce(); - }); - })); - - it('should open one to one chat dialog when button is pressed', fakeAsync(() => { - const createOneToOneChatSpy = jest.spyOn(metisConversationService, 'createOneToOneChat').mockReturnValue(EMPTY); - const chatPartner = new UserPublicInfoDTO(); - chatPartner.login = 'test'; - createConversationDialogTest(chatPartner, OneToOneChatCreateDialogComponent, 'directMessages'); - fixture.whenStable().then(() => { - expect(createOneToOneChatSpy).toHaveBeenCalledOnce(); - }); - })); - - it('should open channel overview dialog when button is pressed', fakeAsync(() => { - fixture.detectChanges(); - tick(301); - const modalService = TestBed.inject(NgbModal); - const mockModalRef = { - componentInstance: { - course: undefined, - createChannelFn: undefined, - initialize: () => {}, - }, - result: Promise.resolve([new GroupChatDTO(), true]), - }; - const openDialogSpy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as unknown as NgbModalRef); - component.onAccordionPlusButtonPressed('generalChannels'); - - tick(301); - fixture.whenStable().then(() => { - expect(openDialogSpy).toHaveBeenCalledOnce(); - expect(openDialogSpy).toHaveBeenCalledWith(ChannelsOverviewDialogComponent, defaultFirstLayerDialogOptions); - expect(mockModalRef.componentInstance.course).toEqual(course); - }); - })); - - function createConversationDialogTest(modalReturnValue: any, dialog: Type, channelType: string) { - fixture.detectChanges(); - tick(301); - const modalService = TestBed.inject(NgbModal); - const mockModalRef = { - componentInstance: { - course: undefined, - initialize: () => {}, - }, - result: Promise.resolve(modalReturnValue), - }; - const openDialogSpy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as unknown as NgbModalRef); - fixture.detectChanges(); - - sidebarEventService.emitSidebarAccordionPlusClickedEvent(channelType); - tick(301); - fixture.whenStable().then(() => { - expect(openDialogSpy).toHaveBeenCalledOnce(); - expect(openDialogSpy).toHaveBeenCalledWith(dialog, defaultFirstLayerDialogOptions); - expect(mockModalRef.componentInstance.course).toEqual(course); - }); - } }); }); diff --git a/src/test/javascript/spec/component/shared/sidebar/accordion-add-options.component.spec.ts b/src/test/javascript/spec/component/shared/sidebar/accordion-add-options.component.spec.ts deleted file mode 100644 index 3bd63df2e611..000000000000 --- a/src/test/javascript/spec/component/shared/sidebar/accordion-add-options.component.spec.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; -import { AccordionAddOptionsComponent } from 'app/shared/sidebar/accordion-add-options/accordion-add-options.component'; -import { MockComponent, MockProvider } from 'ng-mocks'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { FaIconComponent } from '@fortawesome/angular-fontawesome'; -import { NgbCollapseMocksModule } from '../../../helpers/mocks/directive/ngbCollapseMocks.module'; -import { NgbTooltipMocksModule } from '../../../helpers/mocks/directive/ngbTooltipMocks.module'; -import { SidebarEventService } from 'app/shared/sidebar/sidebar-event.service'; - -describe('AccordionAddOptionsComponent', () => { - let component: AccordionAddOptionsComponent; - let fixture: ComponentFixture; - let sidebarEventService: SidebarEventService; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [NgbCollapseMocksModule, NgbTooltipMocksModule], - declarations: [AccordionAddOptionsComponent, MockComponent(FaIconComponent)], - providers: [MockProvider(NgbModal), MockProvider(SidebarEventService)], - }).compileComponents(); - - sidebarEventService = TestBed.inject(SidebarEventService); - fixture = TestBed.createComponent(AccordionAddOptionsComponent); - component = fixture.componentInstance; - - jest.spyOn(sidebarEventService, 'emitSidebarAccordionPlusClickedEvent').mockImplementation(() => {}); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should call emitSidebarAccordionPlusClickedEvent when button is pressed', fakeAsync(() => { - component.groupKey = 'test'; - fixture.detectChanges(); - const emitSidebarAccordionPlusClickedEventSpy = jest.spyOn(sidebarEventService, 'emitSidebarAccordionPlusClickedEvent'); - - const dialogOpenButton = fixture.debugElement.nativeElement.querySelector('#plusButton-test'); - dialogOpenButton.click(); - tick(301); - - expect(emitSidebarAccordionPlusClickedEventSpy).toHaveBeenCalledWith('test'); - })); -}); diff --git a/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts b/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts index bcffb82bc9c1..4e4fdcad7788 100644 --- a/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts +++ b/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts @@ -7,12 +7,11 @@ import { SidebarCardDirective } from 'app/shared/sidebar/sidebar-card.directive' import { SearchFilterPipe } from 'app/shared/pipes/search-filter.pipe'; import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { MockComponent, MockModule, MockPipe } from 'ng-mocks'; +import { MockModule, MockPipe } from 'ng-mocks'; import { DebugElement } from '@angular/core'; import { By } from '@angular/platform-browser'; import { NgbCollapseModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { RouterModule } from '@angular/router'; -import { AccordionAddOptionsComponent } from 'app/shared/sidebar/accordion-add-options/accordion-add-options.component'; describe('SidebarAccordionComponent', () => { let component: SidebarAccordionComponent; @@ -25,7 +24,6 @@ describe('SidebarAccordionComponent', () => { declarations: [ SidebarAccordionComponent, SidebarCardMediumComponent, - MockComponent(AccordionAddOptionsComponent), SidebarCardItemComponent, SidebarCardDirective, SearchFilterPipe, From 8a1d9b33a663fe3b11cd31b7077fc19596fb367c Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Mon, 23 Sep 2024 11:32:26 +0300 Subject: [PATCH 02/25] refactor --- .../channels-overview-dialog.component.ts | 1 - .../conversation-settings.component.html | 2 +- .../conversation-settings.component.ts | 2 +- .../sidebar-card-item.component.html | 2 +- .../sidebar-card-item.component.ts | 2 +- .../app/shared/sidebar/sidebar.component.scss | 1 - ...channels-overview-dialog.component.spec.ts | 48 +------------------ 7 files changed, 6 insertions(+), 52 deletions(-) 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 dc828e521dbc..deb055e00a5e 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,7 +1,6 @@ import { Component, Input, OnDestroy, OnInit } 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'; diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-settings/conversation-settings.component.html b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-settings/conversation-settings.component.html index 752c1e385261..f7f5d5aa0f5a 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-settings/conversation-settings.component.html +++ b/src/main/webapp/app/overview/course-conversations/dialogs/conversation-detail-dialog/tabs/conversation-settings/conversation-settings.component.html @@ -46,7 +46,7 @@ (delete)="deleteChannel()" [dialogError]="dialogError$" > - +
} 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 73013a8e76df..9a9b9b56bb6e 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 @@ -42,7 +42,7 @@ export class ConversationSettingsComponent implements OnInit, OnDestroy { private dialogErrorSource = new Subject(); dialogError$ = this.dialogErrorSource.asObservable(); - readonly faTimes = faTrash; + readonly faTrash = faTrash; conversationAsChannel: ChannelDTO | undefined; canLeaveConversation: boolean; diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.html b/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.html index 519baa631d2d..84467239cee5 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.html @@ -51,7 +51,7 @@ @if (sidebarItem.icon) { } - {{ removeChannelPrefix(sidebarItem, sidebarItem.title) }} @if (sidebarItem.rightIcon) { diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts b/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts index b8d336fe8fa7..dbbbae2325fe 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts @@ -11,7 +11,7 @@ export class SidebarCardItemComponent { @Input() sidebarType?: SidebarTypes; @Input() groupKey?: string; - removeChannelPrefix(item: SidebarCardElement, name: string): string { + removeChannelPrefix(name: string): string { const prefixes = ['exercise-', 'lecture-', 'exam-']; const channelTypes = ['exerciseChannels', 'lectureChannels', 'examChannels']; for (const prefix of prefixes) { diff --git a/src/main/webapp/app/shared/sidebar/sidebar.component.scss b/src/main/webapp/app/shared/sidebar/sidebar.component.scss index 4543cb655086..bd50a08d6776 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar.component.scss +++ b/src/main/webapp/app/shared/sidebar/sidebar.component.scss @@ -57,7 +57,6 @@ .btn-primary { width: 100%; - box-sizing: border-box; } .btn { diff --git a/src/test/javascript/spec/component/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.spec.ts index ac930363fa6a..3cc001fce612 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-dialog.component.spec.ts @@ -12,11 +12,9 @@ import { generateExampleChannelDTO } from '../../helpers/conversationExampleMode import { ChannelService } from 'app/shared/metis/conversations/channel.service'; import { ConversationService } from 'app/shared/metis/conversations/conversation.service'; import { AlertService } from 'app/core/util/alert.service'; -import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { HttpResponse } from '@angular/common/http'; import { By } from '@angular/platform-browser'; -import { ChannelsCreateDialogComponent } from 'app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component'; -import { defaultSecondLayerDialogOptions } from 'app/overview/course-conversations/other/conversation.util'; import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { NgbCollapseMocksModule } from '../../../../../helpers/mocks/directive/ngbCollapseMocks.module'; @@ -107,8 +105,7 @@ examples.forEach((exampleChannel) => { expect(component).toBeTruthy(); expect(getChannelsOfCourseSpy).toHaveBeenCalledWith(course.id); expect(getChannelsOfCourseSpy).toHaveBeenCalledOnce(); - expect(component.noOfChannels).toBe(3); - expect(component.otherChannels).toHaveLength(3); + expect(component.noOfChannels).toBe(6); expect(fixture.nativeElement.querySelectorAll('jhi-channel-item')).toHaveLength(6); expect(channelItems).toHaveLength(6); expect(channelItems[0].channel).toEqual(channelOne); @@ -156,46 +153,5 @@ examples.forEach((exampleChannel) => { expect(component.channelModificationPerformed).toBeTrue(); })); } - - it('should not show create channel button if user is missing the permission', fakeAsync(() => { - canCreateChannel.mockReturnValue(false); - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.querySelector('#createChannel')).toBeFalsy(); - })); - - if (allowChannelCreation) { - it('should open create channel dialog when button is pressed', fakeAsync(() => { - const modalService = TestBed.inject(NgbModal); - const mockModalRef = { - componentInstance: { course: undefined, initialize: () => {} }, - result: Promise.resolve(new ChannelDTO()), - }; - const openDialogSpy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as unknown as NgbModalRef); - fixture.detectChanges(); - - const createChannelButton = fixture.debugElement.nativeElement.querySelector('#createChannel'); - createChannelButton.click(); - tick(501); - fixture.whenStable().then(() => { - expect(openDialogSpy).toHaveBeenCalledOnce(); - expect(openDialogSpy).toHaveBeenCalledWith(ChannelsCreateDialogComponent, defaultSecondLayerDialogOptions); - expect(mockModalRef.componentInstance.course).toEqual(course); - }); - })); - } else { - it('should hide create channel button if creation is not allowed', () => { - const createChannelButton = fixture.debugElement.nativeElement.querySelector('#createChannel'); - expect(createChannelButton).toBeNull(); - }); - } - - it('should toggle other channels on click', () => { - expect(component.otherChannelsAreCollapsed).toBeTrue(); - - const otherChannels = fixture.debugElement.nativeElement.querySelector('.other-channels'); - otherChannels.click(); - - expect(component.otherChannelsAreCollapsed).toBeFalse(); - }); }); }); From 46ec02f05cb27ac8c35b30e30e24516180dd8c04 Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Mon, 23 Sep 2024 12:19:46 +0300 Subject: [PATCH 03/25] refactor --- .../sidebar-accordion.component.spec.ts | 29 +++++-------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts b/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts index 4e4fdcad7788..b14350c46cd7 100644 --- a/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts +++ b/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts @@ -8,15 +8,12 @@ import { SearchFilterPipe } from 'app/shared/pipes/search-filter.pipe'; import { SearchFilterComponent } from 'app/shared/search-filter/search-filter.component'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockModule, MockPipe } from 'ng-mocks'; -import { DebugElement } from '@angular/core'; -import { By } from '@angular/platform-browser'; import { NgbCollapseModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { RouterModule } from '@angular/router'; describe('SidebarAccordionComponent', () => { let component: SidebarAccordionComponent; let fixture: ComponentFixture; - let debugElement: DebugElement; beforeEach(() => { TestBed.configureTestingModule({ @@ -36,7 +33,6 @@ describe('SidebarAccordionComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(SidebarAccordionComponent); component = fixture.componentInstance; - debugElement = fixture.debugElement; component.groupedData = { current: { @@ -69,14 +65,6 @@ describe('SidebarAccordionComponent', () => { expect(component.collapseState[groupKey]).toBeTrue(); }); - it('should toggle collapse state when group header is clicked', () => { - const groupHeader = debugElement.query(By.css('#test-accordion-item-header-current')); - groupHeader.triggerEventHandler('click', null); - fixture.detectChanges(); - - expect(component.collapseState['current']).toBeTrue(); - }); - it('should call expandAll when searchValue changes to a non-empty string', () => { jest.spyOn(component, 'expandAll'); @@ -119,18 +107,17 @@ describe('SidebarAccordionComponent', () => { component.ngOnChanges(); fixture.detectChanges(); - const expectedDisplayedDiv = 2; - const expectedHiddenDiv = 0; - const elementIdDisplayedDiv = `#test-accordion-item-container-${expectedDisplayedDiv}`; - const elementIdHiddenDiv = `#test-accordion-item-container-${expectedHiddenDiv}`; + const displayedDivIndex = 2; + const elementIdDisplayedDiv = `#test-accordion-item-container-${displayedDivIndex}`; const itemDisplayedDiv: HTMLElement = fixture.nativeElement.querySelector(elementIdDisplayedDiv); - const itemHiddeniv: HTMLElement = fixture.nativeElement.querySelector(elementIdHiddenDiv); - // Check if the div exists and has the 'd-none' class - expect(itemDisplayedDiv).toBeTruthy(); // Ensure the element is found + expect(itemDisplayedDiv).toBeTruthy(); expect(itemDisplayedDiv.classList.contains('d-none')).toBeFalse(); - expect(itemHiddeniv).toBeTruthy(); // Ensure the element is found - expect(itemHiddeniv.classList.contains('d-none')).toBeTrue(); + + const elementIdHiddenDiv = `#test-accordion-item-container-0`; + const itemHiddenDiv: HTMLElement = fixture.nativeElement.querySelector(elementIdHiddenDiv); + + expect(itemHiddenDiv).toBeNull(); }); it('should expand the group containing the selected item', () => { From 484ad39ee518207726ce5d0a6f8aed08011d0a85 Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Mon, 23 Sep 2024 13:53:17 +0300 Subject: [PATCH 04/25] refactor --- .../course-conversations.component.spec.ts | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts index 4b52a1ae6bb9..69cd99b318cf 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts @@ -37,6 +37,7 @@ import { GroupChatDTO } from 'app/entities/metis/conversation/group-chat.model'; import { OneToOneChatCreateDialogComponent } from 'app/overview/course-conversations/dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component'; import { 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'; const examples: (ConversationDTO | undefined)[] = [undefined, generateOneToOneChatDTO({}), generateExampleGroupChatDTO({}), generateExampleChannelDTO({})]; @@ -146,14 +147,21 @@ examples.forEach((activeConversation) => { }); describe('Dialog Opening', () => { + const expectedResults = [undefined, true]; + const mockModalRef: Partial = { + componentInstance: { + course: {}, + initialize: jest.fn(), + }, + }; + + beforeEach(() => { + // Set a mock course with id 1 + component.course = { id: 1 } as Course; + }); + it('should open the group chat creation dialog', fakeAsync(() => { - const mockModalRef: Partial = { - componentInstance: { - course: {}, - initialize: jest.fn(), - }, - result: Promise.resolve(undefined), - }; + mockModalRef.result = Promise.resolve(undefined); const spy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as NgbModalRef); component.onCreateGroupChatPressed(); tick(); @@ -161,13 +169,6 @@ examples.forEach((activeConversation) => { })); it('should open the one-to-one chat creation dialog', fakeAsync(() => { - const mockModalRef: Partial = { - componentInstance: { - course: {}, - initialize: jest.fn(), - }, - result: Promise.resolve(undefined), - }; const spy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as NgbModalRef); component.onCreateDirectChatPressed(); tick(); @@ -175,22 +176,25 @@ examples.forEach((activeConversation) => { })); it('should open the channel overview dialog', fakeAsync(() => { - const expectedResults = [undefined, true]; - const mockModalRef: Partial = { - componentInstance: { - course: {}, - createChannelFn: jest.fn(), - initialize: jest.fn(), - channelSubType: undefined, - }, - result: Promise.resolve(expectedResults), - }; + mockModalRef.result = Promise.resolve(expectedResults); const spy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as NgbModalRef); component.onBrowseChannelPressed(); tick(); expect(spy).toHaveBeenCalledWith(ChannelsOverviewDialogComponent, expect.anything()); expect(mockModalRef.componentInstance.initialize).toHaveBeenCalled(); })); + + it('should open the create channel dialog when onCreateChannelPressed is called', fakeAsync(() => { + mockModalRef.result = Promise.resolve(expectedResults); + const spy = jest.spyOn(modalService, 'open').mockReturnValue(mockModalRef as NgbModalRef); + + component.onCreateChannelPressed(); + tick(); + + expect(spy).toHaveBeenCalledWith(ChannelsCreateDialogComponent, expect.anything()); + expect(mockModalRef.componentInstance.course).toEqual(course); + expect(mockModalRef.componentInstance.initialize).toHaveBeenCalled(); + })); }); it('should update thread in post', fakeAsync(() => { From 8b55957db853021d631275aaffd912aeddeaca35 Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Thu, 26 Sep 2024 21:58:27 +0300 Subject: [PATCH 05/25] added new tests --- .../sidebar-accordion.component.spec.ts | 18 ++++++++ .../shared/sidebar/sidebar.component.spec.ts | 44 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts b/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts index b14350c46cd7..efe22f950773 100644 --- a/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts +++ b/src/test/javascript/spec/component/shared/sidebar/sidebar-accordion.component.spec.ts @@ -10,6 +10,8 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; import { MockModule, MockPipe } from 'ng-mocks'; import { NgbCollapseModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap'; import { RouterModule } from '@angular/router'; +import { DebugElement } from '@angular/core'; +import { By } from '@angular/platform-browser'; describe('SidebarAccordionComponent', () => { let component: SidebarAccordionComponent; @@ -65,6 +67,22 @@ describe('SidebarAccordionComponent', () => { expect(component.collapseState[groupKey]).toBeTrue(); }); + it('should toggle collapse state when group header is clicked', () => { + const groupKey = 'current'; + const initialCollapseState = component.collapseState[groupKey]; + + component.searchValue = ''; + fixture.detectChanges(); + + const headerElement: DebugElement = fixture.debugElement.query(By.css('#test-accordion-item-header-' + groupKey)); + expect(headerElement).toBeTruthy(); + + headerElement.triggerEventHandler('click', null); + fixture.detectChanges(); + + expect(component.collapseState[groupKey]).toBe(!initialCollapseState); + }); + it('should call expandAll when searchValue changes to a non-empty string', () => { jest.spyOn(component, 'expandAll'); diff --git a/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts b/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts index 55ad12ae7776..f55840a3b51a 100644 --- a/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts +++ b/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts @@ -173,6 +173,50 @@ describe('SidebarComponent', () => { }); }); + describe('Chat and Channel Creation Methods', () => { + beforeEach(() => { + component.showChatDropdown = true; + component.showChannelDropdown = true; + fixture.detectChanges(); + }); + + it('should emit onDirectChatPressed and set showChatDropdown to false when createDirectChat is called', () => { + jest.spyOn(component.onDirectChatPressed, 'emit'); + + component.createDirectChat(); + + expect(component.onDirectChatPressed.emit).toHaveBeenCalledOnce(); + expect(component.showChatDropdown).toBeFalse(); + }); + + it('should emit onGroupChatPressed and set showChatDropdown to false when createGroupChat is called', () => { + jest.spyOn(component.onGroupChatPressed, 'emit'); + + component.createGroupChat(); + + expect(component.onGroupChatPressed.emit).toHaveBeenCalledOnce(); + expect(component.showChatDropdown).toBeFalse(); + }); + + it('should emit onBrowsePressed and set showChannelDropdown to false when browseChannels is called', () => { + jest.spyOn(component.onBrowsePressed, 'emit'); + + component.browseChannels(); + + expect(component.onBrowsePressed.emit).toHaveBeenCalledOnce(); + expect(component.showChannelDropdown).toBeFalse(); + }); + + it('should emit onCreateChannelPressed and set showChannelDropdown to false when createNewChannel is called', () => { + jest.spyOn(component.onCreateChannelPressed, 'emit'); + + component.createNewChannel(); + + expect(component.onCreateChannelPressed.emit).toHaveBeenCalledOnce(); + expect(component.showChannelDropdown).toBeFalse(); + }); + }); + describe('openFilterExercisesDialog', () => { it('should subscribe to filterApplied from modal', () => { const filterAppliedEmitter = new EventEmitter(); From 87a0c5cae1f8e7bd4b4b50e7ac07a4de55d87ee8 Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Thu, 26 Sep 2024 22:24:17 +0300 Subject: [PATCH 06/25] added new tests --- .../shared/sidebar/sidebar.component.spec.ts | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts b/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts index f55840a3b51a..bce71c7bfc08 100644 --- a/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts +++ b/src/test/javascript/spec/component/shared/sidebar/sidebar.component.spec.ts @@ -173,6 +173,70 @@ describe('SidebarComponent', () => { }); }); + describe('Dropdown Toggle Methods', () => { + beforeEach(() => { + component.showChatDropdown = false; + component.showChannelDropdown = false; + fixture.detectChanges(); + }); + + it('should toggle showChatDropdown and hide showChannelDropdown when toggleChatDropdown is called', () => { + expect(component.showChatDropdown).toBeFalse(); + expect(component.showChannelDropdown).toBeFalse(); + + component.toggleChatDropdown(); + fixture.detectChanges(); + + expect(component.showChatDropdown).toBeTrue(); + expect(component.showChannelDropdown).toBeFalse(); + + component.toggleChatDropdown(); + fixture.detectChanges(); + + expect(component.showChatDropdown).toBeFalse(); + expect(component.showChannelDropdown).toBeFalse(); + }); + + it('should toggle showChannelDropdown and hide showChatDropdown when toggleChannelDropdown is called', () => { + expect(component.showChannelDropdown).toBeFalse(); + expect(component.showChatDropdown).toBeFalse(); + + component.toggleChannelDropdown(); + fixture.detectChanges(); + + expect(component.showChannelDropdown).toBeTrue(); + expect(component.showChatDropdown).toBeFalse(); + + component.toggleChannelDropdown(); + fixture.detectChanges(); + + expect(component.showChannelDropdown).toBeFalse(); + expect(component.showChatDropdown).toBeFalse(); + }); + + it('should hide showChannelDropdown when toggleChatDropdown is called and showChatDropdown becomes true', () => { + component.showChannelDropdown = true; + fixture.detectChanges(); + + component.toggleChatDropdown(); + fixture.detectChanges(); + + expect(component.showChatDropdown).toBeTrue(); + expect(component.showChannelDropdown).toBeFalse(); + }); + + it('should hide showChatDropdown when toggleChannelDropdown is called and showChannelDropdown becomes true', () => { + component.showChatDropdown = true; + fixture.detectChanges(); + + component.toggleChannelDropdown(); + fixture.detectChanges(); + + expect(component.showChannelDropdown).toBeTrue(); + expect(component.showChatDropdown).toBeFalse(); + }); + }); + describe('Chat and Channel Creation Methods', () => { beforeEach(() => { component.showChatDropdown = true; From a556e90dba7740c74752c50344e81904c9f884bc Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Thu, 26 Sep 2024 22:50:05 +0300 Subject: [PATCH 07/25] added new tests --- .../sidebar-card-item.component.ts | 2 +- .../sidebar-card-item.component.spec.ts | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts b/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts index dbbbae2325fe..609ae108bbf7 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-item/sidebar-card-item.component.ts @@ -15,7 +15,7 @@ export class SidebarCardItemComponent { const prefixes = ['exercise-', 'lecture-', 'exam-']; const channelTypes = ['exerciseChannels', 'lectureChannels', 'examChannels']; for (const prefix of prefixes) { - if (name.startsWith(prefix) && channelTypes.includes(this.groupKey)) { + if (name && name.startsWith(prefix) && channelTypes.includes(this.groupKey)) { return name.substring(prefix.length); } } diff --git a/src/test/javascript/spec/component/shared/sidebar/sidebar-card-item.component.spec.ts b/src/test/javascript/spec/component/shared/sidebar/sidebar-card-item.component.spec.ts index e77a8ca29e17..26955ff0b947 100644 --- a/src/test/javascript/spec/component/shared/sidebar/sidebar-card-item.component.spec.ts +++ b/src/test/javascript/spec/component/shared/sidebar/sidebar-card-item.component.spec.ts @@ -37,4 +37,60 @@ describe('SidebarCardItemComponent', () => { const compiled = fixture.debugElement.nativeElement; expect(compiled.querySelector('#test-sidebar-card-title').textContent).toContain(testItem.title); }); + + it('should remove the correct prefix from the name when groupKey is in channelTypes', () => { + const prefixes = ['exercise-', 'lecture-', 'exam-']; + const channelTypes = ['exerciseChannels', 'lectureChannels', 'examChannels']; + + for (let i = 0; i < prefixes.length; i++) { + const prefix = prefixes[i]; + const groupKey = channelTypes[i]; + const nameWithPrefix = prefix + 'TestName'; + + component.groupKey = groupKey; + const result = component.removeChannelPrefix(nameWithPrefix); + + expect(result).toBe('TestName'); + } + }); + + it('should not remove the prefix if groupKey is not in channelTypes', () => { + const nameWithPrefix = 'exercise-TestName'; + component.groupKey = 'otherGroup'; + const result = component.removeChannelPrefix(nameWithPrefix); + + expect(result).toBe(nameWithPrefix); + }); + + it('should not remove the prefix if name does not start with any of the prefixes', () => { + const nameWithoutPrefix = 'TestName'; + component.groupKey = 'exerciseChannels'; + const result = component.removeChannelPrefix(nameWithoutPrefix); + + expect(result).toBe(nameWithoutPrefix); + }); + + it('should handle empty name input', () => { + const emptyName = ''; + component.groupKey = 'exerciseChannels'; + const result = component.removeChannelPrefix(emptyName); + + expect(result).toBe(''); + }); + + it('should handle undefined name input', () => { + const undefinedName = undefined as unknown as string; + component.groupKey = 'exerciseChannels'; + const result = component.removeChannelPrefix(undefinedName); + + expect(result).toBe(undefinedName); + }); + + it('should handle null name input', () => { + const nullName = null as unknown as string; + component.groupKey = 'exerciseChannels'; + const result = component.removeChannelPrefix(nullName); + + expect(result).toBe(nullName); + }); }); From d8dde0c7022c432a17aa02720dffe007693c2c9d Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Thu, 26 Sep 2024 23:37:56 +0300 Subject: [PATCH 08/25] added new tests --- .../course-conversations.component.ts | 17 +++-- .../course-conversations.component.spec.ts | 69 ++++++++++++++++++- 2 files changed, 81 insertions(+), 5 deletions(-) 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 6b950c2c7602..9d44b6d263bc 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 @@ -115,7 +115,7 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { faFilter = faFilter; faSearch = faSearch; - private createChannelFn: (channel: ChannelDTO) => Observable; + createChannelFn?: (channel: ChannelDTO) => Observable; channelActions$ = new Subject(); constructor( @@ -165,9 +165,15 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { this.metisConversationService.checkIsCodeOfConductAccepted(this.course!); this.isServiceSetUp = true; } - this.channelActions$.pipe(debounceTime(500), distinctUntilChanged(), takeUntil(this.ngUnsubscribe)).subscribe((channelAction) => { - this.performChannelAction(channelAction); - }); + this.channelActions$ + .pipe( + debounceTime(500), + distinctUntilChanged((prev, curr) => prev.action === curr.action && prev.channel.id === curr.channel.id), + takeUntil(this.ngUnsubscribe), + ) + .subscribe((channelAction) => { + this.performChannelAction(channelAction); + }); this.createChannelFn = (channel: ChannelDTO) => this.metisConversationService.createChannel(channel); }); @@ -185,6 +191,9 @@ export class CourseConversationsComponent implements OnInit, OnDestroy { complete: () => { this.prepareSidebarData(); }, + error: (error) => { + console.error('Error creating channel:', error); + }, }); } } diff --git a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts index 69cd99b318cf..6322b055b6a5 100644 --- a/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-conversations/course-conversations.component.spec.ts @@ -35,9 +35,10 @@ import { SidebarEventService } from 'app/shared/sidebar/sidebar-event.service'; import { SidebarAccordionComponent } from 'app/shared/sidebar/sidebar-accordion/sidebar-accordion.component'; import { GroupChatDTO } from 'app/entities/metis/conversation/group-chat.model'; import { OneToOneChatCreateDialogComponent } from 'app/overview/course-conversations/dialogs/one-to-one-chat-create-dialog/one-to-one-chat-create-dialog.component'; -import { ChannelsOverviewDialogComponent } from 'app/overview/course-conversations/dialogs/channels-overview-dialog/channels-overview-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 { ChannelDTO } from 'app/entities/metis/conversation/channel.model'; const examples: (ConversationDTO | undefined)[] = [undefined, generateOneToOneChatDTO({}), generateExampleGroupChatDTO({}), generateExampleChannelDTO({})]; @@ -286,5 +287,71 @@ examples.forEach((activeConversation) => { component.onConversationSelected(activeConversation?.id ?? 1); expect(setActiveConversationSpy).toHaveBeenCalled(); }); + + describe('performChannelAction', () => { + let channelAction: ChannelAction; + let channel: ChannelDTO; + + beforeEach(() => { + channel = new ChannelDTO(); + channel.id = 123; + channel.name = 'Test Channel'; + + channelAction = { + action: 'create', + channel, + }; + + component.createChannelFn = undefined; + component.ngOnInit(); + + component.createChannelFn = jest.fn().mockReturnValue(EMPTY); + jest.spyOn(component, 'prepareSidebarData').mockImplementation(); + }); + + it('should call createChannelFn with the provided channel', () => { + component.performChannelAction(channelAction); + expect(component.createChannelFn).toHaveBeenCalledWith(channel); + }); + + it('should call prepareSidebarData on successful completion', () => { + component.createChannelFn = jest.fn().mockReturnValue({ + pipe: () => ({ + subscribe: ({ complete }: any) => { + complete(); + }, + }), + }); + + component.performChannelAction(channelAction); + expect(component.prepareSidebarData).toHaveBeenCalled(); + }); + + it('should log an error if createChannelFn throws an error', () => { + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + + component.createChannelFn = jest.fn().mockReturnValue({ + pipe: () => ({ + subscribe: ({ error }: any) => { + error('Test Error'); + }, + }), + }); + + component.performChannelAction(channelAction); + expect(consoleErrorSpy).toHaveBeenCalledWith('Error creating channel:', 'Test Error'); + + consoleErrorSpy.mockRestore(); + }); + + it('should not call createChannelFn or prepareSidebarData if createChannelFn is undefined', () => { + component.createChannelFn = undefined; + const prepareSidebarDataSpy = jest.spyOn(component, 'prepareSidebarData'); + component.performChannelAction(channelAction); + expect(component.createChannelFn).toBeUndefined(); + // Since createChannelFn is undefined, prepareSidebarData should not be called + expect(prepareSidebarDataSpy).not.toHaveBeenCalled(); + }); + }); }); }); From e6fa965d5adc0ab89ae9a5d2b55219115c60b68c Mon Sep 17 00:00:00 2001 From: Asli Aykan <56061820+asliayk@users.noreply.github.com> Date: Fri, 27 Sep 2024 00:03:51 +0300 Subject: [PATCH 09/25] Update src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.html Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../sidebar/sidebar-card-large/sidebar-card-large.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.html b/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.html index 722876bb9e29..264f9815cfa4 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.html @@ -5,5 +5,6 @@ (click)="emitStoreAndRefresh(sidebarItem.id)" [routerLinkActive]="'bg-selected border-selected'" > +
From d7c7787a935397630d17d866d4e26242f7b394c5 Mon Sep 17 00:00:00 2001 From: Asli Aykan <56061820+asliayk@users.noreply.github.com> Date: Fri, 27 Sep 2024 00:05:02 +0300 Subject: [PATCH 10/25] Update src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../sidebar/sidebar-accordion/sidebar-accordion.component.html | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html b/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html index 16eb4bc62671..2b105c80aea4 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html @@ -17,7 +17,6 @@ - @if ((groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue)?.length) {
@for (sidebarItem of groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue; let last = $last; track sidebarItem) { From f36fc33a6cd7766535fd081e76ac520309c9e13e Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Fri, 27 Sep 2024 00:13:35 +0300 Subject: [PATCH 11/25] refactor --- .../sidebar-card-large/sidebar-card-large.component.ts | 1 + .../sidebar-card-medium/sidebar-card-medium.component.html | 2 +- .../sidebar-card-medium/sidebar-card-medium.component.ts | 1 + .../sidebar-card-small/sidebar-card-small.component.ts | 1 + src/main/webapp/app/shared/sidebar/sidebar-card.directive.ts | 4 +++- 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.ts b/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.ts index 01194afbc0cd..1422a0ad4efc 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-large/sidebar-card-large.component.ts @@ -13,6 +13,7 @@ export class SidebarCardLargeComponent { @Input({ required: true }) sidebarItem: SidebarCardElement; @Input() sidebarType?: SidebarTypes; @Input() itemSelected?: boolean; + /** Key used for grouping or categorizing sidebar items */ @Input() groupKey?: string; constructor( private sidebarEventService: SidebarEventService, diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.html b/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.html index 6307e36257bc..ef12601d7c19 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.html @@ -21,6 +21,6 @@ (click)="emitStoreAndRefresh(sidebarItem.id)" [routerLinkActive]="'bg-selected border-selected'" > - +
} diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.ts b/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.ts index e92e4f94f302..05596e471937 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-medium/sidebar-card-medium.component.ts @@ -16,6 +16,7 @@ export class SidebarCardMediumComponent { @Input() sidebarType?: SidebarTypes; @Input() itemSelected?: boolean; @Output() pageChange = new EventEmitter(); + /** Key used for grouping or categorizing sidebar items */ @Input() groupKey?: string; constructor( diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.ts b/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.ts index 9f44d84aad26..319763c19970 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-card-small/sidebar-card-small.component.ts @@ -16,6 +16,7 @@ export class SidebarCardSmallComponent { @Input({ required: true }) sidebarItem: SidebarCardElement; @Input() sidebarType?: SidebarTypes; @Input() itemSelected?: boolean; + /** Key used for grouping or categorizing sidebar items */ @Input() groupKey?: string; constructor( diff --git a/src/main/webapp/app/shared/sidebar/sidebar-card.directive.ts b/src/main/webapp/app/shared/sidebar/sidebar-card.directive.ts index 4450b7bda49d..ecb219634780 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-card.directive.ts +++ b/src/main/webapp/app/shared/sidebar/sidebar-card.directive.ts @@ -46,7 +46,9 @@ export class SidebarCardDirective implements OnInit, OnDestroy { this.componentRef.instance.itemSelected = this.itemSelected; this.componentRef.instance.sidebarType = this.sidebarType; this.componentRef.instance.sidebarItem = this.sidebarItem; - this.componentRef.instance.groupKey = this.groupKey; + if (this.groupKey !== undefined) { + this.componentRef.instance.groupKey = this.groupKey; + } if (this.size == 'S') { this.componentRef.instance.onUpdateSidebar = this.onUpdateSidebar; } From b80e0411897d5b9656fcd00c018e8f114ebed169 Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Fri, 27 Sep 2024 00:21:37 +0300 Subject: [PATCH 12/25] refactor --- .../sidebar/sidebar-accordion/sidebar-accordion.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html b/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html index 2b105c80aea4..16eb4bc62671 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html +++ b/src/main/webapp/app/shared/sidebar/sidebar-accordion/sidebar-accordion.component.html @@ -17,6 +17,7 @@ + @if ((groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue)?.length) {
@for (sidebarItem of groupedData[groupKey].entityData | searchFilter: ['title', 'type'] : searchValue; let last = $last; track sidebarItem) { From 9fcb02c788acc60dab9b7e77c8e640476c2e30ab Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Fri, 27 Sep 2024 01:27:39 +0300 Subject: [PATCH 13/25] fix color --- src/main/webapp/app/shared/sidebar/sidebar.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/webapp/app/shared/sidebar/sidebar.component.scss b/src/main/webapp/app/shared/sidebar/sidebar.component.scss index bd50a08d6776..4f0a6f282e5f 100644 --- a/src/main/webapp/app/shared/sidebar/sidebar.component.scss +++ b/src/main/webapp/app/shared/sidebar/sidebar.component.scss @@ -84,6 +84,7 @@ cursor: pointer; background-color: #fff; text-align: left; + color: var(--neutral-dark); } .dropdown-item:last-child { From 94308dd2fe33c91d6b5c828987fd30643adcbe15 Mon Sep 17 00:00:00 2001 From: Asli Aykan Date: Tue, 1 Oct 2024 23:07:59 +0300 Subject: [PATCH 14/25] refactor --- .../channels-create-dialog.component.html | 2 +- .../channel-item/channel-item.component.scss | 1 + .../channels-overview-dialog.component.html | 2 +- .../channels-overview-dialog.component.scss | 1 + .../group-chat-create-dialog.component.html | 2 +- ...e-to-one-chat-create-dialog.component.html | 2 +- .../app/shared/sidebar/sidebar.component.html | 20 +++++++++++++++---- .../app/shared/sidebar/sidebar.component.scss | 11 +++++++--- .../app/shared/sidebar/sidebar.component.ts | 6 +++++- 9 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component.html b/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component.html index cd5b295812a3..e7c6413b8b22 100644 --- a/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component.html +++ b/src/main/webapp/app/overview/course-conversations/dialogs/channels-create-dialog/channels-create-dialog.component.html @@ -1,5 +1,5 @@ @if (isInitialized) { -
+