Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

General: Redesign the course overview #9221

Merged
merged 39 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e2bd263
Implement new course card design
edkaya Jul 22, 2024
75efc71
Add management button to course cards
edkaya Jul 24, 2024
1fafd3f
improve recently accessed course cards
edkaya Jul 25, 2024
de4836b
Clean up code
edkaya Jul 25, 2024
b708c01
Merge branch 'develop' into feature/general/redesign-course-cards
edkaya Jul 28, 2024
486d027
Change background
edkaya Jul 29, 2024
4be0641
Fix pie-chart and next exercise icon alignments
edkaya Aug 2, 2024
d5d7999
Merge branch 'develop' into feature/general/redesign-course-cards
edkaya Aug 6, 2024
daf974f
improve course card paddings
edkaya Aug 8, 2024
cda7f60
Add new design of course cards for bigger score icon
edkaya Aug 15, 2024
12052e1
Merge branch 'develop' into feature/general/redesign-course-cards
edkaya Aug 15, 2024
ac61daf
Remove unused code
edkaya Aug 15, 2024
42359ab
Fix paddings in responsive layout
edkaya Aug 19, 2024
9f05883
improve responsiveness
edkaya Aug 19, 2024
3bdfe2f
Merge branch 'develop' into feature/general/redesign-course-cards
edkaya Aug 20, 2024
f0187f9
Fix guided tour test
edkaya Aug 20, 2024
e4ff8af
Remove animation
edkaya Aug 20, 2024
a824b28
Integrate feedback
edkaya Aug 21, 2024
8d93712
Remove comment
edkaya Aug 21, 2024
a3cdde1
adjust styling and alignment of course cards
rabeatwork Aug 22, 2024
f7fd16c
Do further enchancements and polishing
edkaya Aug 23, 2024
b8714dd
Integrate feedback
edkaya Aug 23, 2024
5ea6e84
Improve responsiveness
edkaya Aug 23, 2024
6ac0eaa
fix border issue, make exercise title look clickable
edkaya Aug 26, 2024
5f29b8d
improve header
edkaya Aug 26, 2024
0dbb9c3
Resolve merge conflict
edkaya Aug 26, 2024
0924074
Add breakpoints for card sizes, incorporate feedback
edkaya Aug 28, 2024
2e1e626
Incorporate feedback, display more cards instead of shrinking the cards
edkaya Aug 31, 2024
4a37677
Adjust 3 card row layout better
edkaya Aug 31, 2024
937d675
Merge branch 'develop' into feature/general/redesign-course-cards
edkaya Sep 1, 2024
8c9c2ac
Fix inner border radius
edkaya Sep 2, 2024
203b96e
Display more cards for very large screens
edkaya Sep 2, 2024
42cd8da
Remove empty line
edkaya Sep 2, 2024
9be9933
Use grid layout to omit hardcoded breakpoints
edkaya Sep 3, 2024
59df1ac
Make grid gaps more consistent
edkaya Sep 4, 2024
2cf6930
Reduce min width
edkaya Sep 4, 2024
0bfdf5a
improve layout for smaller screens
edkaya Sep 4, 2024
33fbbf5
Implement feedback
edkaya Sep 8, 2024
25f1274
Merge branch 'develop' into feature/general/redesign-course-cards
edkaya Sep 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/main/webapp/app/course/manage/course-management.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export class CourseManagementService {

private fetchingCoursesForNotifications = false;

private courseOverviewSubject = new BehaviorSubject<boolean>(false);
isCourseOverview$ = this.courseOverviewSubject.asObservable();

constructor(
private http: HttpClient,
private courseStorageService: CourseStorageService,
Expand Down Expand Up @@ -692,4 +695,12 @@ export class CourseManagementService {
// Note: 0 is the default value in case the server returns something that does not make sense
return this.http.get<number>(`${this.resourceUrl}/${courseId}/allowed-complaints?teamMode=${teamMode}`) ?? 0;
}

enableCourseOverviewBackground() {
this.courseOverviewSubject.next(true);
}

disableCourseOverviewBackground() {
this.courseOverviewSubject.next(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const courseOverviewTour: GuidedTour = {
orientation: Orientation.RIGHT,
}),
new TextTourStep({
highlightSelector: '.guided-tour .card-footer',
highlightSelector: '.guided-tour .exercise-guided-tour',
headlineTranslateKey: 'tour.courseOverview.courseFooter.headline',
contentTranslateKey: 'tour.courseOverview.courseFooter.content',
orientation: Orientation.TOPLEFT,
Expand Down
173 changes: 84 additions & 89 deletions src/main/webapp/app/overview/course-card.component.html
Original file line number Diff line number Diff line change
@@ -1,102 +1,97 @@
<div class="card" [class.guided-tour]="hasGuidedTour">
<div id="course-{{ course.id }}-header" class="card-header text-white" [routerLink]="['/courses', course.id!]" [ngStyle]="{ '--background-color-for-hover': courseColor }">
<a class="stretched-link" [routerLink]="['/courses', course.id!]"></a>
<div class="container">
<div class="row d-flex">
<div class="col-2 header-col image-col">
@if (course.courseIcon) {
<jhi-secured-image [cachingStrategy]="CachingStrategy.LOCAL_STORAGE" [src]="course.courseIcon" />
}
<div class="card rounded-4 flex-grow-1" [class.guided-tour]="hasGuidedTour">
<div id="course-{{ course.id }}-header" class="card-header text-white px-3" [routerLink]="['/courses', course.id!]" [ngStyle]="{ '--background-color-for-hover': courseColor }">
<div class="d-flex align-items-center justify-content-between gap-1">
@if (course.courseIcon) {
<div class="col-auto">
<jhi-secured-image [cachingStrategy]="CachingStrategy.LOCAL_STORAGE" [src]="course.courseIcon" />
</div>
<div class="col-7 header-col title-col px-3">
<h5 class="card-title text-center">
{{ course.title }}
</h5>
</div>
<div class="col-2 header-col course-info-col">
<div class="course-info-amounts">
@if (exerciseCount === 0) {
<span>Exercises: {{ exerciseCount }}</span>
}
@if (exerciseCount > 0) {
<a [routerLink]="['/courses', course.id!, 'exercises']">Exercises: {{ exerciseCount }}</a>
}
@if (lectureCount === 0) {
<span>Lectures: {{ lectureCount }}</span>
}
@if (lectureCount > 0) {
<a [routerLink]="['/courses', course.id!, 'lectures']">Lectures: {{ lectureCount }}</a>
}
@if (examCount === 0) {
<span>Exams: {{ examCount }}</span>
}
@if (examCount > 0) {
<a (click)="navigateToExams($event)">Exams: {{ examCount }}</a>
}
</div>
} @else {
<div class="course-circle d-flex align-items-center justify-content-center">
<span class="fs-4">{{ course.title | slice: 0 : 1 }}</span>
</div>
}
<div class="text-center px-2">
<h5 class="card-title">
{{ course.title }}
</h5>
</div>
<div>
<a class="stretched-link" [routerLink]="['/courses', course.id!]"></a>
</div>
</div>
</div>
<div class="card-body">
<div class="card-body rounded-bottom-4 border-lightgrey pt-3">
<a class="stretched-link" [routerLink]="['/courses', course.id!]"></a>
@if (exerciseCount > 0 && (totalReachableScore > 0 || totalAbsoluteScore > 0)) {
<div class="chart-container">
<div class="chart-text">
<h3 class="text-center">{{ totalRelativeScore }}%</h3>
<h6 class="text-center points">{{ totalAbsoluteScore }} / {{ totalReachableScore }} Pts</h6>
</div>
<ngx-charts-pie-chart
id="score-chart"
class="chart-level"
[view]="[200, 200]"
[results]="ngxDoughnutData"
[scheme]="ngxColor"
[doughnut]="true"
[animations]="false"
(select)="onSelect()"
(click)="onSelect()"
>
<ng-template #tooltipTemplate let-model="model">
<span jhiTranslate="artemisApp.courseOverview.statistics.{{ model.name }}"></span>
<span>: {{ model.value }}</span>
</ng-template>
</ngx-charts-pie-chart>
</div>
} @else {
<h6 class="no-statistics" jhiTranslate="artemisApp.studentDashboard.noStatistics"></h6>
}
</div>
@if (nextRelevantExercise) {
<div class="card-footer text-body-secondary">
<a class="stretched-link" [routerLink]="['/courses', course.id!, 'exercises', nextRelevantExercise.id!]"></a>
<div class="container">
<div class="row">
<div class="col-3 next-exercise-col">
<h6 jhiTranslate="artemisApp.studentDashboard.cardExerciseLabel"></h6>
</div>
<div class="col-6 next-exercise-col">
<fa-icon class="next-exercise-icon" [icon]="nextExerciseIcon" placement="right auto" [ngbTooltip]="nextExerciseTooltip | artemisTranslate" />
<span class="next-exercise-title">{{ nextRelevantExercise.title }}</span>
</div>
<div class="col-3 next-exercise-col text-nowrap">
@if (nextExerciseDueDate) {
<div>
<h6>{{ 'artemisApp.exercise.dueDate' | artemisTranslate }}:</h6>
<h6>{{ nextExerciseDueDate | artemisTimeAgo }}</h6>
<div class="d-flex flex-column justify-content-between gap-1">
<div class="information-box-wrapper ms-1">
<div class="row mt-1">
<div class="col-7" [ngClass]="{ 'col-12': !(exerciseCount && (totalReachableScore || totalAbsoluteScore)) }">
@if (exerciseCount && (totalReachableScore || totalAbsoluteScore)) {
<div class="align-self-center">
<div class="fw-bold text-secondary" jhiTranslate="artemisApp.studentDashboard.cardScore"></div>
<div class="fw-bold">
<div
jhiTranslate="artemisApp.studentDashboard.points"
[translateValues]="{
totalAbsoluteScore: totalAbsoluteScore,
totalReachableScore: totalReachableScore,
}"
></div>
</div>
</div>
} @else {
<div class="fw-bold text-secondary" jhiTranslate="artemisApp.studentDashboard.cardScore"></div>
<div class="fw-bold">
<span jhiTranslate="artemisApp.studentDashboard.noStatistics"></span>
</div>
}
<hr />
<div class="fw-bold text-secondary" jhiTranslate="artemisApp.studentDashboard.cardExerciseLabel"></div>
@if (nextRelevantExercise && nextRelevantExercise.id && course.id) {
<a class="fw-bold btn-wrapper exercise-title exercise-guided-tour" [routerLink]="['/courses', course.id, 'exercises', nextRelevantExercise.id]">
{{ nextRelevantExercise.title }}
</a>
} @else {
<div class="fw-bold mb-3 exercise-guided-tour">
<span jhiTranslate="artemisApp.studentDashboard.cardNoExerciseLabel"></span>
</div>
edkaya marked this conversation as resolved.
Show resolved Hide resolved
}
</div>
@if (exerciseCount && (totalReachableScore || totalAbsoluteScore)) {
<div class="align-self-center chart-container col-5 mb-2">
<div class="chart-text">
<h4 class="mb-0">{{ totalRelativeScore }}%</h4>
</div>
<ngx-charts-pie-chart
id="score-chart"
class="chart-level"
style="position: absolute"
[view]="[160, 160]"
[results]="ngxDoughnutData"
[arcWidth]="0.3"
[scheme]="ngxColor"
[doughnut]="true"
[animations]="false"
(select)="onSelect()"
(click)="onSelect()"
>
<ng-template #tooltipTemplate let-model="model">
<span jhiTranslate="artemisApp.courseOverview.statistics.{{ model.name }}"></span>
<span>: {{ model.value }}</span>
</ng-template>
</ngx-charts-pie-chart>
</div>
}
</div>
</div>
@if (course.isAtLeastTutor && course.id) {
<div class="d-flex justify-content-start align-items-center ms-1 mt-1">
<a class="fw-bold btn-wrapper" [routerLink]="['/course-management', course.id]">
<span jhiTranslate="artemisApp.studentDashboard.cardManageCourse"></span>
<fa-icon class="ms-1" [icon]="faArrowRight" />
</a>
</div>
}
</div>
edkaya marked this conversation as resolved.
Show resolved Hide resolved
}
@if (!nextRelevantExercise) {
<div class="card-footer text-body-secondary">
<a class="stretched-link" [routerLink]="['/courses', course.id!]"></a>
<div class="col no-exercise">
<h6 jhiTranslate="artemisApp.studentDashboard.cardNoExerciseLabel"></h6>
</div>
</div>
}
</div>
</div>
30 changes: 4 additions & 26 deletions src/main/webapp/app/overview/course-card.component.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,34 @@
import { Component, Input, OnChanges } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { Color, ScaleType } from '@swimlane/ngx-charts';
import { ARTEMIS_DEFAULT_COLOR } from 'app/app.constants';
import { Course } from 'app/entities/course.model';
import { Exercise, getIcon, getIconTooltip } from 'app/entities/exercise.model';
import { Exercise } from 'app/entities/exercise.model';
import { ExerciseService } from 'app/exercises/shared/exercise/exercise.service';
import { CachingStrategy } from 'app/shared/image/secured-image.component';
import { roundValueSpecifiedByCourseSettings } from 'app/shared/util/utils';
import dayjs from 'dayjs/esm';
import { getExerciseDueDate } from 'app/exercises/shared/exercise/exercise.utils';
import { GraphColors } from 'app/entities/statistics.model';
import { ScoresStorageService } from 'app/course/course-scores/scores-storage.service';
import { ScoreType } from 'app/shared/constants/score-type.constants';
import { CourseScores } from 'app/course/course-scores/course-scores';
import { faArrowRight } from '@fortawesome/free-solid-svg-icons';

@Component({
selector: 'jhi-overview-course-card',
templateUrl: './course-card.component.html',
styleUrls: ['course-card.scss'],
})
export class CourseCardComponent implements OnChanges {
protected readonly faArrowRight = faArrowRight;

readonly ARTEMIS_DEFAULT_COLOR = ARTEMIS_DEFAULT_COLOR;
@Input() course: Course;
@Input() hasGuidedTour: boolean;

CachingStrategy = CachingStrategy;

nextRelevantExercise?: Exercise;
nextExerciseDueDate?: dayjs.Dayjs;
nextExerciseIcon: IconProp;
nextExerciseTooltip: string;
exerciseCount = 0;
lectureCount = 0;
examCount = 0;

totalRelativeScore: number;
totalReachableScore: number;
Expand Down Expand Up @@ -70,9 +65,6 @@ export class CourseCardComponent implements OnChanges {

if (nextExercises.length > 0 && nextExercises[0]) {
this.nextRelevantExercise = nextExercises[0];
this.updateNextDueDate();
this.nextExerciseIcon = getIcon(this.nextRelevantExercise!.type);
this.nextExerciseTooltip = getIconTooltip(this.nextRelevantExercise!.type);
}

const totalScoresForCourse: CourseScores | undefined = this.scoresStorageService.getStoredTotalScores(this.course.id!);
Expand All @@ -89,8 +81,6 @@ export class CourseCardComponent implements OnChanges {
this.ngxDoughnutData = [...this.ngxDoughnutData];
}

this.lectureCount = this.course.numberOfLectures ?? this.course.lectures?.length ?? 0;
this.examCount = this.course.numberOfExams ?? this.course.exams?.length ?? 0;
this.courseColor = this.course.color || this.ARTEMIS_DEFAULT_COLOR;
}

Expand All @@ -110,16 +100,4 @@ export class CourseCardComponent implements OnChanges {
event.stopPropagation();
this.router.navigate(['courses', this.course.id, 'exams']);
}

private updateNextDueDate() {
let nextExerciseDueDate = undefined;
if (this.nextRelevantExercise) {
if (this.nextRelevantExercise.studentParticipations && this.nextRelevantExercise.studentParticipations.length > 0) {
nextExerciseDueDate = getExerciseDueDate(this.nextRelevantExercise, this.nextRelevantExercise.studentParticipations[0]);
} else {
nextExerciseDueDate = this.nextRelevantExercise.dueDate;
}
}
this.nextExerciseDueDate = nextExerciseDueDate;
}
}
Loading
Loading