From 5be8f1a812780fa16ed309d7b7f77de86637d469 Mon Sep 17 00:00:00 2001 From: Paul Rangger Date: Wed, 9 Oct 2024 12:10:53 +0200 Subject: [PATCH 1/7] Tutorial Group Overview Redesign --- .../tutorial-group-detail.component.html | 177 +++++++++++++++++- .../tutorial-group-detail.component.scss | 23 +++ .../tutorial-group-detail.component.ts | 98 ++-------- src/main/webapp/i18n/de/tutorialGroups.json | 5 +- src/main/webapp/i18n/en/tutorialGroups.json | 5 +- 5 files changed, 224 insertions(+), 84 deletions(-) diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html index ced9dd74a469..09a6eb1797e8 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html @@ -5,10 +5,179 @@

{{ tutorialGroup.title }}


} - @if (tutorialDetailSections) { - - } -
+
+
+
+
+
+ + +
+ +
+ +
+
+ @if (tutorialGroup.teachingAssistantImageUrl) { + + } @else { + {{ + tutorInitials + }} + } +
+ +
+ : + {{ tutorialGroup.teachingAssistantName }} +
+ + @if (tutorialGroup.channel && isMessagingEnabled) { +
+ : + @if (tutorialGroup.channel.isMember) { + + {{ tutorialGroup.channel.name }} + + } @else { + {{ tutorialGroup.channel.name }} + } +
+ } +
+
+
+
+ +
+
+
+
+ + +
+ +
+ +
+
+ : + {{ (tutorialGroup.averageAttendance && Math.round(tutorialGroup.averageAttendance)) ?? '-' }} + +
+ +
+ : + {{ tutorialGroup.capacity }} +
+ +
+ : + {{ !tutorialGroup.numberOfRegisteredUsers || tutorialGroup.numberOfRegisteredUsers === 0 ? '-' : tutorialGroup.numberOfRegisteredUsers }} +
+ +
+ : + + + @if (utilization) { +
+
+ {{ utilization }}% +
+
+ } @else { +
+
-
+
+ } +
+
+
+
+
+ +
+
+
+
+ + +
+ +
+ +
+
+ : + {{ tutorialGroup.language }} +
+ +
+ : + {{ tutorialGroup.campus }} +
+ +
+ : + {{ getTutorialTimeSlotString() }} +
+ +
+ : + + {{ tutorialGroup.tutorialGroupSchedule?.location }} +
+ +
+ @if (tutorialGroup.isOnline) { + + + } @else { + + + } +
+
+
+
+
+ + @if (formattedAdditionalInformation) { +
+
+
+
:
+ +
+
+
+
+ } +
+
@if (sessions && sessions.length) { diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.scss b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.scss index c25d9ed8613e..220ad9b71432 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.scss +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.scss @@ -1,3 +1,5 @@ +$tutor-image-size: 4.5rem; + .tutorial-group-detail { .scrollbar { position: relative; @@ -8,4 +10,25 @@ .table-wrapper-scroll-y { display: block; } + + .markdown-preview { + margin-bottom: -1rem; + } +} + +.tutorial-group-detail-tutor-image { + width: $tutor-image-size; + height: $tutor-image-size; + object-fit: cover; +} + +.tutorial-group-detail-tutor-default-image { + width: $tutor-image-size; + height: $tutor-image-size; + font-size: 3rem; + display: inline-flex; + align-items: center; + justify-content: center; + background-color: var(--gray-400); + color: var(--white); } diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts index 65f3b52a53e4..972572c0bbd3 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts @@ -6,9 +6,8 @@ import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { getDayTranslationKey } from '../weekdays'; import { TutorialGroupSession, TutorialGroupSessionStatus } from 'app/entities/tutorial-group/tutorial-group-session.model'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; -import { DetailOverviewSection, DetailType } from 'app/detail-overview-list/detail-overview-list.component'; import { TranslateService } from '@ngx-translate/core'; -import { Detail } from 'app/detail-overview-list/detail.model'; +import { faCircle, faCircleXmark, faLocationDot, faPercent, faUserCheck } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { SortService } from 'app/shared/service/sort.service'; import { getInitialsFromString } from 'app/utils/text.utils'; @@ -32,12 +31,22 @@ export class TutorialGroupDetailComponent implements OnChanges { course: Course; formattedAdditionalInformation?: SafeHtml; - faQuestionCircle = faQuestionCircle; readonly Math = Math; sessions: TutorialGroupSession[] = []; - tutorialDetailSections: DetailOverviewSection[]; + tutorInitials: string; + tutorDefaultProfilePictureHue: string; + isMessagingEnabled: boolean; + utilization: number | undefined; + + // Icons + faUserCheck = faUserCheck; + faPercent = faPercent; + faLocationDot = faLocationDot; + faQuestionCircle = faQuestionCircle; + faCircle = faCircle; + faCircleXmark = faCircleXmark; constructor( private artemisMarkdownService: ArtemisMarkdownService, @@ -60,7 +69,7 @@ export class TutorialGroupDetailComponent implements OnChanges { this.changeDetectorRef.detectChanges(); } } - this.getTutorialDetailSections(); + this.getTutorialDetail(); } getTutorialTimeSlotString(): string | undefined { @@ -79,80 +88,13 @@ export class TutorialGroupDetailComponent implements OnChanges { return `${day} ${start}-${end}, ${repetition}`; } - getTutorialDetailSections() { + getTutorialDetail() { const tutorialGroup = this.tutorialGroup; - const tutorialDetails: Detail[] = [ - { type: DetailType.Link, title: 'artemisApp.entities.tutorialGroup.course', data: { text: tutorialGroup.courseTitle, routerLink: ['../..'] } }, - { type: DetailType.Text, title: 'artemisApp.entities.tutorialGroup.title', data: { text: tutorialGroup.title } }, - { type: DetailType.Text, title: 'artemisApp.entities.tutorialGroup.teachingAssistant', data: { text: tutorialGroup.teachingAssistantName } }, - tutorialGroup.teachingAssistantImageUrl - ? { - type: DetailType.Image, - title: 'artemisApp.entities.tutorialGroup.profilePicture', - data: { imageUrl: tutorialGroup.teachingAssistantImageUrl, altText: 'Profile picture of ' + tutorialGroup.teachingAssistantName }, - } - : { - type: DetailType.DefaultProfilePicture, - title: 'artemisApp.entities.tutorialGroup.profilePicture', - data: { - color: getBackgroundColorHue(tutorialGroup.teachingAssistantId ? tutorialGroup.teachingAssistantId.toString() : 'default'), - initials: getInitialsFromString(tutorialGroup.teachingAssistantName ?? 'NA'), - }, - }, - { - type: DetailType.Text, - title: 'artemisApp.entities.tutorialGroup.utilization', - titleHelpText: 'artemisApp.entities.tutorialGroup.utilizationHelpDetail', - data: { text: tutorialGroup.averageAttendance && tutorialGroup.capacity && Math.round((tutorialGroup.averageAttendance / tutorialGroup.capacity) * 100) }, - }, - { - type: DetailType.Text, - title: 'artemisApp.entities.tutorialGroup.averageAttendanceDetail', - titleHelpText: 'artemisApp.entities.tutorialGroup.averageAttendanceHelpDetail', - data: { text: tutorialGroup.averageAttendance && Math.round(tutorialGroup.averageAttendance) }, - }, - { type: DetailType.Text, title: 'artemisApp.entities.tutorialGroup.capacity', data: { text: tutorialGroup.capacity } }, - { type: DetailType.Text, title: 'artemisApp.entities.tutorialGroup.registrations', data: { text: tutorialGroup.numberOfRegisteredUsers } }, - { type: DetailType.Boolean, title: 'artemisApp.entities.tutorialGroup.isOnline', data: { boolean: tutorialGroup.isOnline } }, - { type: DetailType.Text, title: 'artemisApp.entities.tutorialGroup.language', data: { text: tutorialGroup.language } }, - { type: DetailType.Text, title: 'artemisApp.entities.tutorialGroup.campus', data: { text: tutorialGroup.campus } }, - { type: DetailType.Markdown, title: 'artemisApp.entities.tutorialGroup.additionalInformation', data: { innerHtml: this.formattedAdditionalInformation } }, - { type: DetailType.Text, title: 'artemisApp.entities.tutorialGroup.schedule', data: { text: this.getTutorialTimeSlotString() } }, - ]; - - // inserting optional details in reversed order, so no index calculation is needed - if (tutorialGroup.isOnline) { - tutorialDetails.splice(12, 0, { - type: DetailType.Text, - title: 'artemisApp.forms.scheduleForm.locationInput.labelOnline', - data: { text: tutorialGroup.tutorialGroupSchedule?.location }, - }); - } else { - tutorialDetails.splice(12, 0, { - type: DetailType.Text, - title: 'artemisApp.forms.scheduleForm.locationInput.labelOffline', - data: { text: tutorialGroup.tutorialGroupSchedule?.location }, - }); - } - - if (tutorialGroup.channel && isMessagingEnabled(this.course)) { - tutorialDetails.splice(2, 0, { - type: DetailType.Link, - title: 'artemisApp.entities.tutorialGroup.channel', - data: { - text: tutorialGroup.channel.name, - routerLink: tutorialGroup.channel.isMember ? ['/courses', this.course.id!, 'communication'] : undefined, - queryParams: { conversationId: tutorialGroup.channel.id }, - }, - }); - } - this.tutorialDetailSections = [ - { - headline: 'artemisApp.pages.courseTutorialGroupDetail.sections.general', - details: tutorialDetails, - }, - ]; + this.tutorDefaultProfilePictureHue = getBackgroundColorHue(tutorialGroup.teachingAssistantId ? tutorialGroup.teachingAssistantId.toString() : 'default'); + this.tutorInitials = getInitialsFromString(tutorialGroup.teachingAssistantName ?? 'NA'); + this.isMessagingEnabled = isMessagingEnabled(this.course); + this.utilization = tutorialGroup.averageAttendance && tutorialGroup.capacity && Math.round((tutorialGroup.averageAttendance / tutorialGroup.capacity) * 100); } recalculateAttendanceDetails() { @@ -166,6 +108,6 @@ export class TutorialGroupDetailComponent implements OnChanges { this.tutorialGroup.averageAttendance = Math.round(averageAttendance); } - this.getTutorialDetailSections(); + this.getTutorialDetail(); } } diff --git a/src/main/webapp/i18n/de/tutorialGroups.json b/src/main/webapp/i18n/de/tutorialGroups.json index 021d7a6de739..6ba6bf79b1ef 100644 --- a/src/main/webapp/i18n/de/tutorialGroups.json +++ b/src/main/webapp/i18n/de/tutorialGroups.json @@ -55,7 +55,10 @@ "averageAttendanceDetail": "Durchschnittliche Anwesenheit", "averageAttendanceHelpDetail": "Durchschnittliche Anwesenheit in den letzten drei Sitzungen. Falls keine Anwesenheit eingetragen wurde, wird die entsprechende Sitzung ignoriert und die Berechnung mit zwei, bzw. einer Sitzung durchgeführt.", "profilePicture": "Tutor:in Profilbild", - "profilePictureAlt": "Profilbild von {{ user }}" + "profilePictureAlt": "Profilbild von {{ user }}", + "name": "Name", + "information": "Information", + "isNotOnline": "Nicht Online" }, "tutorialGroupSchedule": { "dayOfWeek": "Wochentag", diff --git a/src/main/webapp/i18n/en/tutorialGroups.json b/src/main/webapp/i18n/en/tutorialGroups.json index 7792eb659f6d..cc6b966db947 100644 --- a/src/main/webapp/i18n/en/tutorialGroups.json +++ b/src/main/webapp/i18n/en/tutorialGroups.json @@ -55,7 +55,10 @@ "averageAttendanceDetail": "Average Attendance", "averageAttendanceHelpDetail": "Average attendance in the last three sessions. If no attendance is entered, the corresponding session is ignored and the calculation is performed with two or one session.", "profilePicture": "Tutor Profile Picture", - "profilePictureAlt": "Profile picture of {{ user }}" + "profilePictureAlt": "Profile picture of {{ user }}", + "name": "Name", + "information": "Information", + "isNotOnline": "Not Online" }, "tutorialGroupSchedule": { "dayOfWeek": "Day of Week", From 1d3f7e229bdef67a15e537d4dad6c5f849191449 Mon Sep 17 00:00:00 2001 From: Paul Rangger Date: Wed, 9 Oct 2024 12:43:16 +0200 Subject: [PATCH 2/7] Fix runs of component --- .../shared/tutorial-group-detail.component.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts b/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts index 02d89da561e4..38674f635110 100644 --- a/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts +++ b/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts @@ -1,7 +1,7 @@ import { TutorialGroupDetailComponent } from 'app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe'; -import { MockComponent, MockPipe, MockProvider } from 'ng-mocks'; +import { MockComponent, MockDirective, MockPipe, MockProvider } from 'ng-mocks'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { generateExampleTutorialGroup } from '../helpers/tutorialGroupExampleModels'; import { ChangeDetectorRef, Component, Input, ViewChild } from '@angular/core'; @@ -11,7 +11,6 @@ import { FaIconComponent } from '@fortawesome/angular-fontawesome'; import { NgbTooltipMocksModule } from '../../../helpers/mocks/directive/ngbTooltipMocks.module'; import { TutorialGroupUtilizationIndicatorComponent } from 'app/course/tutorial-groups/shared/tutorial-group-utilization-indicator/tutorial-group-utilization-indicator.component'; import { RemoveSecondsPipe } from 'app/course/tutorial-groups/shared/remove-seconds.pipe'; -import { DetailOverviewListComponent } from 'app/detail-overview-list/detail-overview-list.component'; import { MockTranslateService } from '../../../helpers/mocks/service/mock-translate.service'; import { TranslateService } from '@ngx-translate/core'; import { provideHttpClientTesting } from '@angular/common/http/testing'; @@ -21,6 +20,7 @@ import dayjs from 'dayjs/esm'; import { TutorialGroupSessionStatus } from 'app/entities/tutorial-group/tutorial-group-session.model'; import { provideHttpClient } from '@angular/common/http'; import { RouterModule } from '@angular/router'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; @Component({ selector: 'jhi-mock-header', template: '
' }) class MockHeaderComponent { @@ -60,13 +60,13 @@ describe('TutorialGroupDetailWrapperTest', () => { imports: [NgbTooltipMocksModule, RouterModule.forRoot([])], declarations: [ TutorialGroupDetailComponent, - DetailOverviewListComponent, MockWrapperComponent, MockHeaderComponent, MockPipe(ArtemisTranslatePipe), MockPipe(RemoveSecondsPipe), MockComponent(FaIconComponent), MockComponent(TutorialGroupUtilizationIndicatorComponent), + MockDirective(TranslateDirective), ], providers: [ provideHttpClient(), @@ -78,7 +78,6 @@ describe('TutorialGroupDetailWrapperTest', () => { { provide: LocalStorageService, useClass: MockLocalStorageService }, ], }) - .overrideTemplate(DetailOverviewListComponent, '') .compileComponents() .then(() => { fixture = TestBed.createComponent(MockWrapperComponent); @@ -116,6 +115,7 @@ describe('TutorialGroupDetailComponent', () => { MockComponent(FaIconComponent), MockComponent(TutorialGroupUtilizationIndicatorComponent), MockPipe(RemoveSecondsPipe), + MockDirective(TranslateDirective), ], providers: [MockProvider(ArtemisMarkdownService), { provide: TranslateService, useClass: MockTranslateService }, MockProvider(ChangeDetectorRef)], }) From 9ea17ea64d63e42e6c4104914354d513107347d5 Mon Sep 17 00:00:00 2001 From: Paul Rangger Date: Wed, 9 Oct 2024 18:25:16 +0200 Subject: [PATCH 3/7] Created component for icon card --- .../tutorial-group-detail.component.html | 217 ++++++++---------- .../tutorial-group-detail.component.ts | 5 +- .../shared/tutorial-groups-shared.module.ts | 3 +- .../shared/icon-card/icon-card.component.html | 12 + .../shared/icon-card/icon-card.component.scss | 0 .../shared/icon-card/icon-card.component.ts | 20 ++ .../icon-card/icon-card.component.spec.ts | 24 ++ .../tutorial-group-detail.component.spec.ts | 3 + 8 files changed, 154 insertions(+), 130 deletions(-) create mode 100644 src/main/webapp/app/shared/icon-card/icon-card.component.html create mode 100644 src/main/webapp/app/shared/icon-card/icon-card.component.scss create mode 100644 src/main/webapp/app/shared/icon-card/icon-card.component.ts create mode 100644 src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html index 09a6eb1797e8..862b82483c07 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html @@ -7,162 +7,127 @@

{{ tutorialGroup.title }}

}
-
-
-
- - + +
+
+ @if (tutorialGroup.teachingAssistantImageUrl) { + + } @else { + {{ + tutorInitials + }} + }
-
+
+ : + {{ tutorialGroup.teachingAssistantName }} +
-
-
- @if (tutorialGroup.teachingAssistantImageUrl) { - + @if (tutorialGroup.channel && isMessagingEnabled) { +
+ : + @if (tutorialGroup.channel.isMember) { + + {{ tutorialGroup.channel.name }} + } @else { - {{ - tutorInitials - }} + {{ tutorialGroup.channel.name }} }
- -
- : - {{ tutorialGroup.teachingAssistantName }} -
- - @if (tutorialGroup.channel && isMessagingEnabled) { -
- : - @if (tutorialGroup.channel.isMember) { - - {{ tutorialGroup.channel.name }} - - } @else { - {{ tutorialGroup.channel.name }} - } -
- } -
+ }
-
+
-
-
-
- - + +
+
+ : + {{ (tutorialGroup.averageAttendance && Math.round(tutorialGroup.averageAttendance)) ?? '-' }} +
-
-
-
- : - {{ (tutorialGroup.averageAttendance && Math.round(tutorialGroup.averageAttendance)) ?? '-' }} - -
- -
- : - {{ tutorialGroup.capacity }} -
- -
- : - {{ !tutorialGroup.numberOfRegisteredUsers || tutorialGroup.numberOfRegisteredUsers === 0 ? '-' : tutorialGroup.numberOfRegisteredUsers }} -
+ : + {{ tutorialGroup.capacity }} +
-
- : - +
+ : + {{ !tutorialGroup.numberOfRegisteredUsers || tutorialGroup.numberOfRegisteredUsers === 0 ? '-' : tutorialGroup.numberOfRegisteredUsers }} +
- @if (utilization) { -
-
- {{ utilization }}% -
-
- } @else { -
-
-
+
+ : + + + @if (utilization) { +
+
+ {{ utilization }}%
- } -
+
+ } @else { +
+
-
+
+ }
-
+
-
-
-
- - + +
+
+ : + {{ tutorialGroup.language }}
-
-
-
- : - {{ tutorialGroup.language }} -
- -
- : - {{ tutorialGroup.campus }} -
+ : + {{ tutorialGroup.campus }} +
-
- : - {{ getTutorialTimeSlotString() }} -
+
+ : + {{ getTutorialTimeSlotString() }} +
-
- : +
+ : - {{ tutorialGroup.tutorialGroupSchedule?.location }} -
+ {{ tutorialGroup.tutorialGroupSchedule?.location }} +
-
- @if (tutorialGroup.isOnline) { - - - } @else { - - - } -
+
+ @if (tutorialGroup.isOnline) { + + + } @else { + + + }
-
+
@if (formattedAdditionalInformation) { diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts index 972572c0bbd3..21e346c9a259 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts @@ -5,9 +5,8 @@ import { SafeHtml } from '@angular/platform-browser'; import { ArtemisMarkdownService } from 'app/shared/markdown.service'; import { getDayTranslationKey } from '../weekdays'; import { TutorialGroupSession, TutorialGroupSessionStatus } from 'app/entities/tutorial-group/tutorial-group-session.model'; -import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; import { TranslateService } from '@ngx-translate/core'; -import { faCircle, faCircleXmark, faLocationDot, faPercent, faUserCheck } from '@fortawesome/free-solid-svg-icons'; +import { faCircle, faCircleInfo, faCircleXmark, faPercent, faQuestionCircle, faUserCheck } from '@fortawesome/free-solid-svg-icons'; import dayjs from 'dayjs/esm'; import { SortService } from 'app/shared/service/sort.service'; import { getInitialsFromString } from 'app/utils/text.utils'; @@ -43,7 +42,7 @@ export class TutorialGroupDetailComponent implements OnChanges { // Icons faUserCheck = faUserCheck; faPercent = faPercent; - faLocationDot = faLocationDot; + faCircleInfo = faCircleInfo; faQuestionCircle = faQuestionCircle; faCircle = faCircle; faCircleXmark = faCircleXmark; diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-shared.module.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-shared.module.ts index 6dc2bf654604..072ea574195e 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-shared.module.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-groups-shared.module.ts @@ -13,9 +13,10 @@ import { TutorialGroupUtilizationIndicatorComponent } from './tutorial-group-uti import { RemoveSecondsPipe } from 'app/course/tutorial-groups/shared/remove-seconds.pipe'; import { MeetingPatternPipe } from 'app/course/tutorial-groups/shared/meeting-pattern.pipe'; import { DetailModule } from 'app/detail-overview-list/detail.module'; +import { IconCardComponent } from 'app/shared/icon-card/icon-card.component'; @NgModule({ - imports: [ArtemisSharedModule, RouterModule, ArtemisSidePanelModule, VerticalProgressBarModule, DetailModule], + imports: [ArtemisSharedModule, RouterModule, ArtemisSidePanelModule, VerticalProgressBarModule, DetailModule, IconCardComponent], declarations: [ TutorialGroupsTableComponent, TutorialGroupDetailComponent, diff --git a/src/main/webapp/app/shared/icon-card/icon-card.component.html b/src/main/webapp/app/shared/icon-card/icon-card.component.html new file mode 100644 index 000000000000..52333d6b7829 --- /dev/null +++ b/src/main/webapp/app/shared/icon-card/icon-card.component.html @@ -0,0 +1,12 @@ +
+
+
+ + +
+ +
+ + +
+
diff --git a/src/main/webapp/app/shared/icon-card/icon-card.component.scss b/src/main/webapp/app/shared/icon-card/icon-card.component.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/main/webapp/app/shared/icon-card/icon-card.component.ts b/src/main/webapp/app/shared/icon-card/icon-card.component.ts new file mode 100644 index 000000000000..7ac3f22c9097 --- /dev/null +++ b/src/main/webapp/app/shared/icon-card/icon-card.component.ts @@ -0,0 +1,20 @@ +import { Component, input } from '@angular/core'; +import { faCircleInfo } from '@fortawesome/free-solid-svg-icons'; +import { IconDefinition } from '@fortawesome/fontawesome-common-types'; +import { ArtemisSharedCommonModule } from 'app/shared/shared-common.module'; +import { ArtemisSharedPipesModule } from 'app/shared/pipes/shared-pipes.module'; + +@Component({ + selector: 'jhi-icon-card', + templateUrl: './icon-card.component.html', + styleUrl: './icon-card.component.scss', + standalone: true, + imports: [ArtemisSharedCommonModule, ArtemisSharedPipesModule], +}) +export class IconCardComponent { + headerIcon = input(faCircleInfo); + + headline = input('Title'); + + constructor() {} +} diff --git a/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts b/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts new file mode 100644 index 000000000000..22f52c0abdec --- /dev/null +++ b/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { MockDirective } from 'ng-mocks'; +import { IconCardComponent } from 'app/shared/icon-card/icon-card.component'; +import { TranslateDirective } from 'app/shared/language/translate.directive'; + +describe('IconCardComponent', () => { + let component: IconCardComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [IconCardComponent], + declarations: [MockDirective(TranslateDirective)], + }).compileComponents(); + + fixture = TestBed.createComponent(IconCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts b/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts index 38674f635110..1290e595fbd3 100644 --- a/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts +++ b/src/test/javascript/spec/component/tutorial-groups/shared/tutorial-group-detail.component.spec.ts @@ -21,6 +21,7 @@ import { TutorialGroupSessionStatus } from 'app/entities/tutorial-group/tutorial import { provideHttpClient } from '@angular/common/http'; import { RouterModule } from '@angular/router'; import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { IconCardComponent } from 'app/shared/icon-card/icon-card.component'; @Component({ selector: 'jhi-mock-header', template: '
' }) class MockHeaderComponent { @@ -62,6 +63,7 @@ describe('TutorialGroupDetailWrapperTest', () => { TutorialGroupDetailComponent, MockWrapperComponent, MockHeaderComponent, + MockComponent(IconCardComponent), MockPipe(ArtemisTranslatePipe), MockPipe(RemoveSecondsPipe), MockComponent(FaIconComponent), @@ -113,6 +115,7 @@ describe('TutorialGroupDetailComponent', () => { TutorialGroupDetailComponent, MockPipe(ArtemisTranslatePipe), MockComponent(FaIconComponent), + MockComponent(IconCardComponent), MockComponent(TutorialGroupUtilizationIndicatorComponent), MockPipe(RemoveSecondsPipe), MockDirective(TranslateDirective), From a5c38249a43fe7321c7878f9b4a281849a9d9a73 Mon Sep 17 00:00:00 2001 From: Paul Rangger Date: Wed, 9 Oct 2024 22:07:02 +0200 Subject: [PATCH 4/7] Changed font size of default picture --- .../tutorial-group-detail/tutorial-group-detail.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.scss b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.scss index 220ad9b71432..0a2501554636 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.scss +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.scss @@ -25,7 +25,7 @@ $tutor-image-size: 4.5rem; .tutorial-group-detail-tutor-default-image { width: $tutor-image-size; height: $tutor-image-size; - font-size: 3rem; + font-size: 2rem; display: inline-flex; align-items: center; justify-content: center; From fc3e2d1f3864d7c8d9335ddf1119f5605652b65b Mon Sep 17 00:00:00 2001 From: Paul Rangger Date: Thu, 10 Oct 2024 10:15:21 +0200 Subject: [PATCH 5/7] Codebunny feedback --- .../tutorial-group-detail.component.ts | 6 +++++- .../shared/icon-card/icon-card.component.html | 4 ++-- .../shared/icon-card/icon-card.component.ts | 2 -- .../icon-card/icon-card.component.spec.ts | 20 ++++++++++++++++++- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts index 21e346c9a259..1cedec4583be 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts @@ -93,7 +93,11 @@ export class TutorialGroupDetailComponent implements OnChanges { this.tutorDefaultProfilePictureHue = getBackgroundColorHue(tutorialGroup.teachingAssistantId ? tutorialGroup.teachingAssistantId.toString() : 'default'); this.tutorInitials = getInitialsFromString(tutorialGroup.teachingAssistantName ?? 'NA'); this.isMessagingEnabled = isMessagingEnabled(this.course); - this.utilization = tutorialGroup.averageAttendance && tutorialGroup.capacity && Math.round((tutorialGroup.averageAttendance / tutorialGroup.capacity) * 100); + if (tutorialGroup.averageAttendance && tutorialGroup.capacity) { + this.utilization = Math.round((tutorialGroup.averageAttendance / tutorialGroup.capacity) * 100); + } else { + this.utilization = undefined; + } } recalculateAttendanceDetails() { diff --git a/src/main/webapp/app/shared/icon-card/icon-card.component.html b/src/main/webapp/app/shared/icon-card/icon-card.component.html index 52333d6b7829..11aabaa71cdc 100644 --- a/src/main/webapp/app/shared/icon-card/icon-card.component.html +++ b/src/main/webapp/app/shared/icon-card/icon-card.component.html @@ -1,8 +1,8 @@ -
+
- +

diff --git a/src/main/webapp/app/shared/icon-card/icon-card.component.ts b/src/main/webapp/app/shared/icon-card/icon-card.component.ts index 7ac3f22c9097..dc0c603000a2 100644 --- a/src/main/webapp/app/shared/icon-card/icon-card.component.ts +++ b/src/main/webapp/app/shared/icon-card/icon-card.component.ts @@ -15,6 +15,4 @@ export class IconCardComponent { headerIcon = input(faCircleInfo); headline = input('Title'); - - constructor() {} } diff --git a/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts b/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts index 22f52c0abdec..30175418aac5 100644 --- a/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts +++ b/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts @@ -2,6 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MockDirective } from 'ng-mocks'; import { IconCardComponent } from 'app/shared/icon-card/icon-card.component'; import { TranslateDirective } from 'app/shared/language/translate.directive'; +import { faCircleInfo, faCoffee } from '@fortawesome/free-solid-svg-icons'; describe('IconCardComponent', () => { let component: IconCardComponent; @@ -19,6 +20,23 @@ describe('IconCardComponent', () => { }); it('should create', () => { - expect(component).toBeTruthy(); + expect(component).not.toBeNull(); + }); + + it('should display the default headerIcon and headline', () => { + expect(component.headerIcon()).toBe(faCircleInfo); + expect(component.headline()).toBe('Title'); + + fixture.detectChanges(); + }); + + it('should display custom headerIcon and headline when inputs are set', () => { + fixture.componentRef.setInput('headerIcon', faCoffee); + fixture.componentRef.setInput('headline', 'Test'); + + fixture.detectChanges(); + + expect(component.headerIcon()).toBe(faCoffee); + expect(component.headline()).toBe('Test'); }); }); From 90fd4f8162c595f577d9235ccf59c4a913a6fb05 Mon Sep 17 00:00:00 2001 From: Paul Rangger Date: Thu, 10 Oct 2024 10:31:01 +0200 Subject: [PATCH 6/7] Switched from imports to declarations --- .../component/shared/icon-card/icon-card.component.spec.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts b/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts index 30175418aac5..d96d5417b943 100644 --- a/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts +++ b/src/test/javascript/spec/component/shared/icon-card/icon-card.component.spec.ts @@ -10,8 +10,8 @@ describe('IconCardComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [IconCardComponent], - declarations: [MockDirective(TranslateDirective)], + imports: [], + declarations: [IconCardComponent, MockDirective(TranslateDirective)], }).compileComponents(); fixture = TestBed.createComponent(IconCardComponent); @@ -26,8 +26,6 @@ describe('IconCardComponent', () => { it('should display the default headerIcon and headline', () => { expect(component.headerIcon()).toBe(faCircleInfo); expect(component.headline()).toBe('Title'); - - fixture.detectChanges(); }); it('should display custom headerIcon and headline when inputs are set', () => { From 0de2b91ab2cda99670a2a48544bae7d0d3d46992 Mon Sep 17 00:00:00 2001 From: Paul Rangger Date: Thu, 10 Oct 2024 14:27:59 +0200 Subject: [PATCH 7/7] Made icons readonly and moved function from template to init --- .../tutorial-group-detail.component.html | 2 +- .../tutorial-group-detail.component.ts | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html index 862b82483c07..26607ec88597 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.html @@ -102,7 +102,7 @@

{{ tutorialGroup.title }}

: - {{ getTutorialTimeSlotString() }} + {{ tutorialTimeslotString }}
diff --git a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts index 1cedec4583be..2d2624ac55a4 100644 --- a/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts +++ b/src/main/webapp/app/course/tutorial-groups/shared/tutorial-group-detail/tutorial-group-detail.component.ts @@ -35,17 +35,18 @@ export class TutorialGroupDetailComponent implements OnChanges { sessions: TutorialGroupSession[] = []; tutorInitials: string; + tutorialTimeslotString: string | undefined; tutorDefaultProfilePictureHue: string; isMessagingEnabled: boolean; utilization: number | undefined; // Icons - faUserCheck = faUserCheck; - faPercent = faPercent; - faCircleInfo = faCircleInfo; - faQuestionCircle = faQuestionCircle; - faCircle = faCircle; - faCircleXmark = faCircleXmark; + readonly faUserCheck = faUserCheck; + readonly faPercent = faPercent; + readonly faCircleInfo = faCircleInfo; + readonly faQuestionCircle = faQuestionCircle; + readonly faCircle = faCircle; + readonly faCircleXmark = faCircleXmark; constructor( private artemisMarkdownService: ArtemisMarkdownService, @@ -98,6 +99,7 @@ export class TutorialGroupDetailComponent implements OnChanges { } else { this.utilization = undefined; } + this.tutorialTimeslotString = this.getTutorialTimeSlotString(); } recalculateAttendanceDetails() {