-
Notifications
You must be signed in to change notification settings - Fork 301
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
: Display recently accessed courses on top
#7827
Conversation
WalkthroughThe recent updates introduce a service to track and store data on course access within an Angular application. This service is integrated across various components and templates, enabling the display of recently accessed courses and the sorting of courses into 'recent' and 'regular' categories. The changes enhance the user interface by providing users with a quick view of their last accessed courses, improving navigation and course management efficiency. Changes
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChat with CodeRabbit Bot (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review Status
Actionable comments generated: 6
Configuration used: CodeRabbit UI
Files ignored due to filter (4)
- src/main/webapp/i18n/de/course.json
- src/main/webapp/i18n/de/student-dashboard.json
- src/main/webapp/i18n/en/course.json
- src/main/webapp/i18n/en/student-dashboard.json
Files selected for processing (12)
- src/main/webapp/app/course/course-access-storage.service.ts (1 hunks)
- src/main/webapp/app/course/manage/course-management-tab-bar/course-management-tab-bar.component.ts (3 hunks)
- src/main/webapp/app/course/manage/course-management.component.html (2 hunks)
- src/main/webapp/app/course/manage/course-management.component.ts (3 hunks)
- src/main/webapp/app/overview/course-overview.component.ts (3 hunks)
- src/main/webapp/app/overview/courses.component.html (1 hunks)
- src/main/webapp/app/overview/courses.component.ts (4 hunks)
- src/test/javascript/spec/component/course/course-management-tab-bar.component.spec.ts (5 hunks)
- src/test/javascript/spec/component/course/course-management.component.spec.ts (3 hunks)
- src/test/javascript/spec/component/course/course-overview.component.spec.ts (7 hunks)
- src/test/javascript/spec/component/course/course.component.spec.ts (4 hunks)
- src/test/javascript/spec/service/course-access-storage.service.spec.ts (1 hunks)
Additional comments: 26
src/main/webapp/app/course/course-access-storage.service.ts (2)
10-10: The constructor is correctly injecting the
LocalStorageService
for use within the service.25-31: The method
getLastAccessedCourses
correctly retrieves and sorts the courses by last access time. However, it assumes that the course IDs are stored as object keys, which are always strings. Ensure that the conversion from string to number (Number(entry[0])
) does not lead to any unexpected behavior if non-numeric keys are ever introduced.src/test/javascript/spec/service/course-access-storage.service.spec.ts (1)
- 25-30: The test 'should store accessed course' correctly verifies that the
onCourseAccessed
method stores the course access information.src/main/webapp/app/course/manage/course-management.component.html (1)
- 41-51: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [33-48]
The new condition to display a message for the 'recent' semester is implemented correctly. However, ensure that the translation keys like
'artemisApp.course.recentlyAccessed'
are defined in the translation files to avoid missing translations.src/main/webapp/app/course/manage/course-management-tab-bar/course-management-tab-bar.component.ts (1)
- 110-118: > Note: This review was outside the patches, so it was mapped to the patch with the greatest overlap. Original lines [89-115]
The addition of
CourseAccessStorageService
toCourseManagementTabBarComponent
and its usage inngOnInit
is implemented correctly. However, ensure that thecourseId
variable is always defined before it's used inngOnInit
, as it relies on the route parameters being available.src/test/javascript/spec/component/course/course-management-tab-bar.component.spec.ts (1)
- 88-95: The new test case 'should register changes in course and notify courseAccessStorageService on init' correctly verifies that the service is called during component initialization. Ensure that the
onCourseAccessed
method is mocked if it has side effects or makes HTTP requests to prevent flaky tests.src/test/javascript/spec/component/course/course-management.component.spec.ts (4)
27-27: The import of
CourseAccessStorageService
is correctly added to the test file.106-110: The
CourseAccessStorageService
is correctly provided in the TestBed configuration. Ensure that the mock of this service behaves as expected in the test environment.143-153: The test case for sorting unique semester names appears to be correct and should validate the sorting logic effectively.
156-177: The test case for sorting courses into semesters now includes the logic for recently accessed courses. It's important to verify that the mock for
getLastAccessedCourses
returns the expected values to ensure the test is valid.src/main/webapp/app/course/manage/course-management.component.ts (3)
14-14: The
CourseAccessStorageService
is correctly imported in the component file.50-50: The
CourseAccessStorageService
is correctly added to the constructor for dependency injection.141-174: The logic for sorting courses into semesters now includes handling for recently accessed courses. Ensure that the
getLastAccessedCourses
method fromCourseAccessStorageService
is being used correctly and that the sorting logic is sound.src/main/webapp/app/overview/course-overview.component.ts (3)
37-37: The
CourseAccessStorageService
is correctly imported in the component file.114-114: The
CourseAccessStorageService
is correctly added to the constructor for dependency injection.125-125: The call to
onCourseAccessed
is correctly placed within thengOnInit
lifecycle hook to track when a course is accessed. Ensure that this method is called every time the component is initialized with a new course.src/test/javascript/spec/component/course/course.component.spec.ts (4)
39-39: The import of
CourseAccessStorageService
is correctly added to support the new functionality.112-112: The addition of
courseAccessStorageService
as a member variable in theCoursesComponent
class is consistent with the use of dependency injection in Angular.141-141: The
CourseAccessStorageService
is correctly provided as a mock provider in the test module configuration, which is necessary for isolating the unit tests.149-149: The
courseAccessStorageService
is correctly injected usingTestBed.inject
, which is the standard way to get a handle on injected services in Angular tests.src/test/javascript/spec/component/course/course-overview.component.spec.ts (6)
52-52: The import of
CourseAccessStorageService
is correctly added to support the new functionality.77-77: The explicit type declaration for
exams
asExam[]
improves type safety and clarity in the test setup.124-124: The addition of
courseAccessStorageService
as a member variable in theCourseOverviewComponent
tests is consistent with the use of dependency injection in Angular.174-174: The
CourseAccessStorageService
is correctly provided as a mock provider in the test module configuration, which is necessary for isolating the unit tests.192-192: The
courseAccessStorageService
is correctly injected usingTestBed.inject
, which is the standard way to get a handle on injected services in Angular tests.218-218: The test case correctly simulates a course access event by calling
courseAccessStorageService.onCourseAccessed
. This is important for verifying that the component notifies the service about course access, which is part of the new feature's functionality.
it('should sort courses into regular and recently accessed after loading', () => { | ||
const findAllForDashboardSpy = jest.spyOn(courseService, 'findAllForDashboard'); | ||
const sortCoursesInRecentlyAccessedAndRegularCoursesSpy = jest.spyOn(component, 'sortCoursesInRecentlyAccessedAndRegularCourses'); | ||
findAllForDashboardSpy.mockReturnValue(of(new HttpResponse({ body: coursesDashboard, headers: new HttpHeaders() }))); | ||
|
||
component.ngOnInit(); | ||
|
||
expect(findAllForDashboardSpy).toHaveBeenCalledOnce(); | ||
expect(sortCoursesInRecentlyAccessedAndRegularCoursesSpy).toHaveBeenCalledOnce(); | ||
|
||
const lastAccessedCourses = [1, 2]; | ||
const recentCoursesSpy = jest.spyOn(courseAccessStorageService, 'getLastAccessedCourses').mockReturnValue(lastAccessedCourses); | ||
|
||
// Test for less than 5 courses | ||
const courses = []; | ||
for (let i = 1; i <= 3; i++) { | ||
const course = { id: i }; | ||
courses.push(course); | ||
} | ||
|
||
component.courses = courses; | ||
component.sortCoursesInRecentlyAccessedAndRegularCourses(); | ||
expect(component.regularCourses).toEqual(courses); | ||
expect(component.recentlyAccessedCourses).toEqual([]); | ||
expect(recentCoursesSpy).not.toHaveBeenCalled(); | ||
|
||
// Test for more than 5 courses | ||
for (let i = 4; i <= 7; i++) { | ||
const course = { id: i }; | ||
courses.push(course); | ||
} | ||
component.courses = courses; | ||
component.sortCoursesInRecentlyAccessedAndRegularCourses(); | ||
expect(component.regularCourses).toEqual(courses.slice(2)); | ||
expect(component.recentlyAccessedCourses).toEqual(courses.slice(0, 2)); | ||
expect(recentCoursesSpy).toHaveBeenCalledOnce(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new test case checks the sorting of courses into regular and recently accessed categories. However, there are a few points to consider:
- The test case seems to be testing two different scenarios (less than 5 courses and more than 5 courses). It would be better to split this into two separate test cases for clarity and maintainability.
- The
recentCoursesSpy
is set up after thecomponent.courses
is already assigned andsortCoursesInRecentlyAccessedAndRegularCourses
is called. The spy should be set up before these actions to capture any calls during the method execution. - The test case is directly manipulating the
component.courses
property. It would be more robust to use the service's public API to simulate user interaction or lifecycle events that lead to the sorting logic being triggered.
- const recentCoursesSpy = jest.spyOn(courseAccessStorageService, 'getLastAccessedCourses').mockReturnValue(lastAccessedCourses);
+ const recentCoursesSpy = jest.spyOn(courseAccessStorageService, 'getLastAccessedCourses');
+ recentCoursesSpy.mockReturnValue(lastAccessedCourses);
Committable suggestion
❗ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
it('should sort courses into regular and recently accessed after loading', () => { | |
const findAllForDashboardSpy = jest.spyOn(courseService, 'findAllForDashboard'); | |
const sortCoursesInRecentlyAccessedAndRegularCoursesSpy = jest.spyOn(component, 'sortCoursesInRecentlyAccessedAndRegularCourses'); | |
findAllForDashboardSpy.mockReturnValue(of(new HttpResponse({ body: coursesDashboard, headers: new HttpHeaders() }))); | |
component.ngOnInit(); | |
expect(findAllForDashboardSpy).toHaveBeenCalledOnce(); | |
expect(sortCoursesInRecentlyAccessedAndRegularCoursesSpy).toHaveBeenCalledOnce(); | |
const lastAccessedCourses = [1, 2]; | |
const recentCoursesSpy = jest.spyOn(courseAccessStorageService, 'getLastAccessedCourses').mockReturnValue(lastAccessedCourses); | |
// Test for less than 5 courses | |
const courses = []; | |
for (let i = 1; i <= 3; i++) { | |
const course = { id: i }; | |
courses.push(course); | |
} | |
component.courses = courses; | |
component.sortCoursesInRecentlyAccessedAndRegularCourses(); | |
expect(component.regularCourses).toEqual(courses); | |
expect(component.recentlyAccessedCourses).toEqual([]); | |
expect(recentCoursesSpy).not.toHaveBeenCalled(); | |
// Test for more than 5 courses | |
for (let i = 4; i <= 7; i++) { | |
const course = { id: i }; | |
courses.push(course); | |
} | |
component.courses = courses; | |
component.sortCoursesInRecentlyAccessedAndRegularCourses(); | |
expect(component.regularCourses).toEqual(courses.slice(2)); | |
expect(component.recentlyAccessedCourses).toEqual(courses.slice(0, 2)); | |
expect(recentCoursesSpy).toHaveBeenCalledOnce(); | |
it('should sort courses into regular and recently accessed after loading', () => { | |
const findAllForDashboardSpy = jest.spyOn(courseService, 'findAllForDashboard'); | |
const sortCoursesInRecentlyAccessedAndRegularCoursesSpy = jest.spyOn(component, 'sortCoursesInRecentlyAccessedAndRegularCourses'); | |
findAllForDashboardSpy.mockReturnValue(of(new HttpResponse({ body: coursesDashboard, headers: new HttpHeaders() }))); | |
component.ngOnInit(); | |
expect(findAllForDashboardSpy).toHaveBeenCalledOnce(); | |
expect(sortCoursesInRecentlyAccessedAndRegularCoursesSpy).toHaveBeenCalledOnce(); | |
const lastAccessedCourses = [1, 2]; | |
const recentCoursesSpy = jest.spyOn(courseAccessStorageService, 'getLastAccessedCourses'); | |
recentCoursesSpy.mockReturnValue(lastAccessedCourses); | |
// Test for less than 5 courses | |
const courses = []; | |
for (let i = 1; i <= 3; i++) { | |
const course = { id: i }; | |
courses.push(course); | |
} | |
component.courses = courses; | |
component.sortCoursesInRecentlyAccessedAndRegularCourses(); | |
expect(component.regularCourses).toEqual(courses); | |
expect(component.recentlyAccessedCourses).toEqual([]); | |
expect(recentCoursesSpy).not.toHaveBeenCalled(); | |
// Test for more than 5 courses | |
for (let i = 4; i <= 7; i++) { | |
const course = { id: i }; | |
courses.push(course); | |
} | |
component.courses = courses; | |
component.sortCoursesInRecentlyAccessedAndRegularCourses(); | |
expect(component.regularCourses).toEqual(courses.slice(2)); | |
expect(component.recentlyAccessedCourses).toEqual(courses.slice(0, 2)); | |
expect(recentCoursesSpy).toHaveBeenCalledOnce(); |
onCourseAccessed(courseId: number): void { | ||
const courseAccessMap: { [key: number]: number } = this.localStorage.retrieve(CourseAccessStorageService.STORAGE_KEY) || {}; | ||
|
||
courseAccessMap[courseId] = Date.now(); | ||
|
||
if (Object.keys(courseAccessMap).length > 3) { | ||
const oldestEntry = Object.entries(courseAccessMap).reduce((prev, curr) => (prev[1] < curr[1] ? prev : curr)); | ||
delete courseAccessMap[oldestEntry[0]]; | ||
} | ||
|
||
this.localStorage.store(CourseAccessStorageService.STORAGE_KEY, courseAccessMap); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method onCourseAccessed
correctly updates the timestamp for the accessed course. However, the logic to limit the stored courses to 3 is not scalable if the requirements change to store more or fewer courses. Consider making the limit a configurable property of the service.
- if (Object.keys(courseAccessMap).length > 3) {
+ const MAX_STORED_COURSES = 3; // This can be moved to a configurable part of the service
+ if (Object.keys(courseAccessMap).length > MAX_STORED_COURSES) {
Committable suggestion
❗ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
onCourseAccessed(courseId: number): void { | |
const courseAccessMap: { [key: number]: number } = this.localStorage.retrieve(CourseAccessStorageService.STORAGE_KEY) || {}; | |
courseAccessMap[courseId] = Date.now(); | |
if (Object.keys(courseAccessMap).length > 3) { | |
const oldestEntry = Object.entries(courseAccessMap).reduce((prev, curr) => (prev[1] < curr[1] ? prev : curr)); | |
delete courseAccessMap[oldestEntry[0]]; | |
} | |
this.localStorage.store(CourseAccessStorageService.STORAGE_KEY, courseAccessMap); | |
} | |
onCourseAccessed(courseId: number): void { | |
const MAX_STORED_COURSES = 3; // This can be moved to a configurable part of the service | |
const courseAccessMap: { [key: number]: number } = this.localStorage.retrieve(CourseAccessStorageService.STORAGE_KEY) || {}; | |
courseAccessMap[courseId] = Date.now(); | |
if (Object.keys(courseAccessMap).length > MAX_STORED_COURSES) { | |
const oldestEntry = Object.entries(courseAccessMap).reduce((prev, curr) => (prev[1] < curr[1] ? prev : curr)); | |
delete courseAccessMap[oldestEntry[0]]; | |
} | |
this.localStorage.store(CourseAccessStorageService.STORAGE_KEY, courseAccessMap); | |
} |
@@ -219,6 +224,7 @@ describe('CourseOverviewComponent', () => { | |||
expect(getCourseStub).toHaveBeenCalled(); | |||
expect(subscribeForQuizChangesStub).toHaveBeenCalledOnce(); | |||
expect(subscribeToTeamAssignmentUpdatesStub).toHaveBeenCalledOnce(); | |||
expect(notifyAboutCourseAccessStub).toHaveBeenCalledExactlyOnceWith(course1.id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test case should verify that courseAccessStorageService.onCourseAccessed
is called with the correct course ID. It's important to ensure that the right course is being marked as accessed in the service.
- expect(notifyAboutCourseAccessStub).toHaveBeenCalledExactlyOnceWith(course1.id);
+ expect(notifyAboutCourseAccessStub).toHaveBeenCalledWith(course1.id);
Committable suggestion
❗ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
expect(notifyAboutCourseAccessStub).toHaveBeenCalledExactlyOnceWith(course1.id); | |
expect(notifyAboutCourseAccessStub).toHaveBeenCalledWith(course1.id); |
it('should retrieve last accessed courses and remove older courses', fakeAsync(() => { | ||
const courseIds = [123, 456, 789, 101112, 7494]; | ||
courseIds.forEach((courseId) => { | ||
service.onCourseAccessed(courseId); | ||
tick(10); // Wait 10ms to ensure that the timestamp is different for each course | ||
}); | ||
const lastAccessedCourses = service.getLastAccessedCourses(); | ||
expect(lastAccessedCourses).toEqual(courseIds.reverse().slice(0, 3)); | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test 'should retrieve last accessed courses and remove older courses' correctly simulates accessing multiple courses and checks if only the 3 most recent accesses are kept. However, the test relies on a hardcoded delay (tick(10)
) to ensure timestamps are different. This could be made more robust by explicitly setting different timestamps in the test.
- tick(10); // Wait 10ms to ensure that the timestamp is different for each course
+ // Set explicit timestamps to avoid reliance on tick
+ courseAccessMap[courseId] = Date.now() + courseId;
Committable suggestion
❗ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
it('should retrieve last accessed courses and remove older courses', fakeAsync(() => { | |
const courseIds = [123, 456, 789, 101112, 7494]; | |
courseIds.forEach((courseId) => { | |
service.onCourseAccessed(courseId); | |
tick(10); // Wait 10ms to ensure that the timestamp is different for each course | |
}); | |
const lastAccessedCourses = service.getLastAccessedCourses(); | |
expect(lastAccessedCourses).toEqual(courseIds.reverse().slice(0, 3)); | |
})); | |
it('should retrieve last accessed courses and remove older courses', fakeAsync(() => { | |
const courseIds = [123, 456, 789, 101112, 7494]; | |
courseIds.forEach((courseId) => { | |
service.onCourseAccessed(courseId); | |
// Set explicit timestamps to avoid reliance on tick | |
courseAccessMap[courseId] = Date.now() + courseId; | |
}); | |
const lastAccessedCourses = service.getLastAccessedCourses(); | |
expect(lastAccessedCourses).toEqual(courseIds.reverse().slice(0, 3)); | |
})); |
@if (recentlyAccessedCourses.length > 0) { | ||
<div class="row"> | ||
<h4 class="col mb-3 fw-medium" jhiTranslate="artemisApp.studentDashboard.recentlyAccessed">Recently Accessed Courses</h4> | ||
</div> | ||
<div class="row"> | ||
@for (course of recentlyAccessedCourses; track course) { | ||
<jhi-overview-course-card class="col-12 col-lg-6 col-xl-4 pe-2 ps-2 mb-3" [course]="course" [hasGuidedTour]="course === courseForGuidedTour"> | ||
</jhi-overview-course-card> | ||
} | ||
</div> | ||
@if (regularCourses.length > 0) { | ||
<div class="row"> | ||
<h4 class="col mb-3 fw-medium" jhiTranslate="artemisApp.studentDashboard.otherCourses">Other Courses</h4> | ||
</div> | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The conditional rendering for 'Recently Accessed Courses' and 'Other Courses' is implemented correctly using Angular's structural directives. Ensure that the trackBy
function is implemented for the ngFor
directive to improve performance by minimizing DOM manipulations.
- @for (course of recentlyAccessedCourses; track course) {
+ @for (course of recentlyAccessedCourses; trackBy: trackCourse) {
Committable suggestion
❗ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
@if (recentlyAccessedCourses.length > 0) { | |
<div class="row"> | |
<h4 class="col mb-3 fw-medium" jhiTranslate="artemisApp.studentDashboard.recentlyAccessed">Recently Accessed Courses</h4> | |
</div> | |
<div class="row"> | |
@for (course of recentlyAccessedCourses; track course) { | |
<jhi-overview-course-card class="col-12 col-lg-6 col-xl-4 pe-2 ps-2 mb-3" [course]="course" [hasGuidedTour]="course === courseForGuidedTour"> | |
</jhi-overview-course-card> | |
} | |
</div> | |
@if (regularCourses.length > 0) { | |
<div class="row"> | |
<h4 class="col mb-3 fw-medium" jhiTranslate="artemisApp.studentDashboard.otherCourses">Other Courses</h4> | |
</div> | |
} | |
} | |
@if (recentlyAccessedCourses.length > 0) { | |
<div class="row"> | |
<h4 class="col mb-3 fw-medium" jhiTranslate="artemisApp.studentDashboard.recentlyAccessed">Recently Accessed Courses</h4> | |
</div> | |
<div class="row"> | |
@for (course of recentlyAccessedCourses; trackBy: trackCourse) { | |
<jhi-overview-course-card class="col-12 col-lg-6 col-xl-4 pe-2 ps-2 mb-3" [course]="course" [hasGuidedTour]="course === courseForGuidedTour"> | |
</jhi-overview-course-card> | |
} | |
</div> | |
@if (regularCourses.length > 0) { | |
<div class="row"> | |
<h4 class="col mb-3 fw-medium" jhiTranslate="artemisApp.studentDashboard.otherCourses">Other Courses</h4> | |
</div> | |
} | |
} |
/** | ||
* Sorts the courses into recently accessed and regular courses. | ||
* If there are less than 5 courses, all courses are displayed in the regular courses section. | ||
*/ | ||
sortCoursesInRecentlyAccessedAndRegularCourses() { | ||
if (this.courses.length <= 5) { | ||
this.regularCourses = this.courses; | ||
} else { | ||
const lastAccessedCourseIds = this.courseAccessStorageService.getLastAccessedCourses(); | ||
this.recentlyAccessedCourses = this.courses.filter((course) => lastAccessedCourseIds.includes(course.id!)); | ||
this.regularCourses = this.courses.filter((course) => !lastAccessedCourseIds.includes(course.id!)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method sortCoursesInRecentlyAccessedAndRegularCourses
is implemented correctly. However, consider adding error handling for cases where course.id
might be undefined to prevent runtime errors.
- this.recentlyAccessedCourses = this.courses.filter((course) => lastAccessedCourseIds.includes(course.id!));
+ this.recentlyAccessedCourses = this.courses.filter((course) => course.id && lastAccessedCourseIds.includes(course.id));
Committable suggestion
❗ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation.
/** | |
* Sorts the courses into recently accessed and regular courses. | |
* If there are less than 5 courses, all courses are displayed in the regular courses section. | |
*/ | |
sortCoursesInRecentlyAccessedAndRegularCourses() { | |
if (this.courses.length <= 5) { | |
this.regularCourses = this.courses; | |
} else { | |
const lastAccessedCourseIds = this.courseAccessStorageService.getLastAccessedCourses(); | |
this.recentlyAccessedCourses = this.courses.filter((course) => lastAccessedCourseIds.includes(course.id!)); | |
this.regularCourses = this.courses.filter((course) => !lastAccessedCourseIds.includes(course.id!)); | |
} | |
} | |
/** | |
* Sorts the courses into recently accessed and regular courses. | |
* If there are less than 5 courses, all courses are displayed in the regular courses section. | |
*/ | |
sortCoursesInRecentlyAccessedAndRegularCourses() { | |
if (this.courses.length <= 5) { | |
this.regularCourses = this.courses; | |
} else { | |
const lastAccessedCourseIds = this.courseAccessStorageService.getLastAccessedCourses(); | |
this.recentlyAccessedCourses = this.courses.filter((course) => course.id && lastAccessedCourseIds.includes(course.id)); | |
this.regularCourses = this.courses.filter((course) => !lastAccessedCourseIds.includes(course.id!)); | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checklist
General
Client
authorities
to all new routes and checked the course groups for displaying navigation elements (links, buttons).Motivation and Context
As a student or instructor with a lot of courses, especially users administrating an Artemis instance, you can have a hard time to find the courses that are currently relevant to you. In particular, during any given semester you're likely to access a small set of courses frequently; however, you still have to crawl the entire list of courses to find them, wasting a lot of time. Therefore, we should speed up that process.
Description
Added a service that stores the last access timestamp for a course in the local storage and provides a list of the most recently accessed courses to other components.
In the course management view, Artemis will list the 3 most recently accessed courses in a separate "semester" right at the start.
In the course overview, Artemis will display the boxes of the 3 most recently accessed courses first if there are more than 5 courses for a user.
Steps for Testing
Prerequisites:
Testserver States
Note
These badges show the state of the test servers.
Green = Currently available, Red = Currently locked
Review Progress
Performance Review
Code Review
Manual Tests
Exam Mode Test
Test Coverage
Screenshots
Summary by CodeRabbit
New Features
Enhancements
Tests