diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseLearnerProfileRepository.java b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseLearnerProfileRepository.java index d3755a88c7a3..6ff5860c6baa 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseLearnerProfileRepository.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/repository/CourseLearnerProfileRepository.java @@ -48,6 +48,7 @@ public interface CourseLearnerProfileRepository extends ArtemisJpaRepository findByLoginAndCourse(@Param("login") String login, @Param("course") Course course); diff --git a/src/main/java/de/tum/cit/aet/artemis/atlas/service/profile/CourseLearnerProfileService.java b/src/main/java/de/tum/cit/aet/artemis/atlas/service/profile/CourseLearnerProfileService.java index b903954d5077..a4926393e591 100644 --- a/src/main/java/de/tum/cit/aet/artemis/atlas/service/profile/CourseLearnerProfileService.java +++ b/src/main/java/de/tum/cit/aet/artemis/atlas/service/profile/CourseLearnerProfileService.java @@ -1,5 +1,6 @@ package de.tum.cit.aet.artemis.atlas.service.profile; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -35,6 +36,7 @@ public CourseLearnerProfileService(CourseLearnerProfileRepository courseLearnerP /** * Create a course learner profile for a user and saves it in the database + * If a profile already exists for this user and course, it returns the existing profile. * * @param course the course for which the profile is created * @param user the user for which the profile is created @@ -42,6 +44,12 @@ public CourseLearnerProfileService(CourseLearnerProfileRepository courseLearnerP */ public CourseLearnerProfile createCourseLearnerProfile(Course course, User user) { + // Check if a profile already exists for this user and course + Optional existingProfile = courseLearnerProfileRepository.findByLoginAndCourse(user.getLogin(), course); + if (existingProfile.isPresent()) { + return existingProfile.get(); + } + // Ensure that the user has a learner profile (lazy creation) if (user.getLearnerProfile() == null) { learnerProfileService.createProfile(user); diff --git a/src/main/webapp/app/core/user/settings/learner-profile/feedback-learner-profile/feedback-learner-profile.component.html b/src/main/webapp/app/core/user/settings/learner-profile/feedback-learner-profile/feedback-learner-profile.component.html index 79eb40f198be..fca0cd212afc 100644 --- a/src/main/webapp/app/core/user/settings/learner-profile/feedback-learner-profile/feedback-learner-profile.component.html +++ b/src/main/webapp/app/core/user/settings/learner-profile/feedback-learner-profile/feedback-learner-profile.component.html @@ -15,17 +15,17 @@

-
+
-
+
- +
-
+
- +
diff --git a/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.html b/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.html index 261b59fcb6f5..a3a7742e80c1 100644 --- a/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.html +++ b/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.html @@ -1,8 +1,8 @@
- @for (option of options; track option.value) { - }
diff --git a/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.scss b/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.scss index 3210177b5c46..01fb1347bb54 100644 --- a/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.scss +++ b/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.scss @@ -5,4 +5,26 @@ .btn-group { display: flex; width: 100%; + align-items: stretch; +} + +.btn-group .btn { + flex: 1; + min-width: 0; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + padding-top: 0.625rem; /* 10px */ + padding-bottom: 0.625rem; /* 10px */ +} + +.btn-group .btn .label { + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + white-space: normal; /* allow wrap */ + line-height: 1.2; + -webkit-line-clamp: 2; /* default clamp to 2 lines */ } diff --git a/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.spec.ts b/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.spec.ts index 94c171547663..5103cd922a7d 100644 --- a/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.spec.ts +++ b/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.spec.ts @@ -22,33 +22,29 @@ describe('SegmentedToggleComponent', () => { fixture = TestBed.createComponent(SegmentedToggleComponent); component = fixture.componentInstance; - component.options = mockOptions; + fixture.componentRef.setInput('options', mockOptions); fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); - expect(component.options).toEqual(mockOptions); + expect(component.options()).toEqual(mockOptions); }); it('should bind selected value correctly', () => { - component.selected = 2; + component.selected.set(2); fixture.detectChanges(); - expect(component.selected).toBe(2); + expect(component.selected()).toBe(2); }); it('should emit selectedChange event when an option is selected', () => { const selectedValue = 1; - const spy = jest.spyOn(component.selectedChange, 'emit'); - component.select(selectedValue); - - expect(spy).toHaveBeenCalledWith(selectedValue); - expect(component.selected).toBe(selectedValue); + expect(component.selected()).toBe(selectedValue); }); it('should handle empty options array', () => { - component.options = []; + fixture.componentRef.setInput('options', []); fixture.detectChanges(); const compiled = fixture.nativeElement; @@ -57,7 +53,7 @@ describe('SegmentedToggleComponent', () => { }); it('should render all options correctly', () => { - component.options = mockOptions; + fixture.componentRef.setInput('options', mockOptions); fixture.detectChanges(); const compiled = fixture.nativeElement; @@ -70,8 +66,8 @@ describe('SegmentedToggleComponent', () => { }); it('should apply selected class to the active option', () => { - component.options = mockOptions; - component.selected = 2; + fixture.componentRef.setInput('options', mockOptions); + component.selected.set(2); fixture.detectChanges(); const compiled = fixture.nativeElement; diff --git a/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.ts b/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.ts index 29714cb435b3..1f6b6ef9ec45 100644 --- a/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.ts +++ b/src/main/webapp/app/shared/segmented-toggle/segmented-toggle.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component, input, model } from '@angular/core'; import { NgClass } from '@angular/common'; @Component({ @@ -9,12 +9,11 @@ import { NgClass } from '@angular/common'; styleUrls: ['./segmented-toggle.component.scss'], }) export class SegmentedToggleComponent { - @Input() options: { label: string; value: T }[] = []; - @Input() selected: T; - @Output() selectedChange = new EventEmitter(); + options = input<{ label: string; value: T }[]>([]); + selected = model(); + maxLines = input(2); select(value: T) { - this.selected = value; - this.selectedChange.emit(value); + this.selected.set(value); } } diff --git a/src/main/webapp/i18n/de/learnerProfile.json b/src/main/webapp/i18n/de/learnerProfile.json index d7bf05990f54..cdf29e7854cb 100644 --- a/src/main/webapp/i18n/de/learnerProfile.json +++ b/src/main/webapp/i18n/de/learnerProfile.json @@ -47,7 +47,7 @@ "friendly": "Freundlich" }, "profileSaved": "Feedback-Lernprofil gespeichert", - "invalidRange": "Werte müssen true oder false sein", + "invalidRange": "Werte müssen zwischen 1 und 3 liegen", "error": "Feedback-Lernprofil konnte nicht gespeichert werden", "setPreferencesButton": "Feedback-Präferenzen festlegen", "replayOnboardingButton": "Onboarding wiederholen" diff --git a/src/main/webapp/i18n/en/learnerProfile.json b/src/main/webapp/i18n/en/learnerProfile.json index d8aac15787c8..25a30f2851e1 100644 --- a/src/main/webapp/i18n/en/learnerProfile.json +++ b/src/main/webapp/i18n/en/learnerProfile.json @@ -47,7 +47,7 @@ "friendly": "Friendly" }, "profileSaved": "Feedback learner profile saved", - "invalidRange": "Values must be true or false", + "invalidRange": "Values must be between 1 and 3", "error": "Feedback learner profile could not be saved", "setPreferencesButton": "Set your feedback preferences", "replayOnboardingButton": "Replay onboarding"