-
Notifications
You must be signed in to change notification settings - Fork 302
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
General: Add filter for exercises (#8858)
- Loading branch information
1 parent
cd2b3a8
commit 5298910
Showing
50 changed files
with
2,518 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,30 @@ | ||
export class ExerciseCategory { | ||
public color?: string; | ||
|
||
// TODO should be renamed to "name" -> accessing variable via "category.name" instead of "category.category" - requires database migration (stored as json in database, see the table "exercise_categories") | ||
public category?: string; | ||
|
||
constructor() {} | ||
constructor(category: string | undefined, color: string | undefined) { | ||
this.color = color; | ||
this.category = category; | ||
} | ||
|
||
equals(otherExerciseCategory: ExerciseCategory): boolean { | ||
return this.color === otherExerciseCategory.color && this.category === otherExerciseCategory.category; | ||
} | ||
|
||
/** | ||
* @param otherExerciseCategory | ||
* @returns the alphanumerical order of the two exercise categories based on their display text | ||
*/ | ||
compare(otherExerciseCategory: ExerciseCategory): number { | ||
if (this.category === otherExerciseCategory.category) { | ||
return 0; | ||
} | ||
|
||
const displayText = this.category?.toLowerCase() ?? ''; | ||
const otherCategoryDisplayText = otherExerciseCategory.category?.toLowerCase() ?? ''; | ||
|
||
return displayText < otherCategoryDisplayText ? -1 : 1; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
src/main/webapp/app/overview/course-exercises/course-exercises.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
...e-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<h4 class="fw-medium"> | ||
<div class="badge me-1" [ngStyle]="{ backgroundColor: category.color }" [ngClass]="'category-' + fontSize"> | ||
{{ category.category }} | ||
@if (displayRemoveButton) { | ||
<button class="remove-button" (click)="onClick()"> | ||
<fa-icon [icon]="faTimes" class="category-chip-remove" /> | ||
</button> | ||
} | ||
</div> | ||
</h4> |
18 changes: 18 additions & 0 deletions
18
...e-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
.remove-button { | ||
color: var(--white); | ||
background: transparent !important; | ||
border: none !important; | ||
|
||
&:hover { | ||
opacity: 0.8; | ||
} | ||
} | ||
|
||
/** category options should be consistent with the type CategoryFontSize */ | ||
.category-small { | ||
font-size: 0.85rem; | ||
} | ||
|
||
.category-default { | ||
font-size: 1rem; | ||
} |
23 changes: 23 additions & 0 deletions
23
...ise-categories/custom-exercise-category-badge/custom-exercise-category-badge.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { Component, Input } from '@angular/core'; | ||
import type { ExerciseCategory } from 'app/entities/exercise-category.model'; | ||
import { CommonModule } from '@angular/common'; | ||
import { faTimes } from '@fortawesome/free-solid-svg-icons'; | ||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; | ||
|
||
type CategoryFontSize = 'default' | 'small'; | ||
|
||
@Component({ | ||
selector: 'jhi-custom-exercise-category-badge', | ||
templateUrl: './custom-exercise-category-badge.component.html', | ||
styleUrls: ['custom-exercise-category-badge.component.scss'], | ||
standalone: true, | ||
imports: [CommonModule, FontAwesomeModule], | ||
}) | ||
export class CustomExerciseCategoryBadgeComponent { | ||
protected readonly faTimes = faTimes; | ||
|
||
@Input({ required: true }) category: ExerciseCategory; | ||
@Input() displayRemoveButton: boolean = false; | ||
@Input() onClick: () => void = () => {}; | ||
@Input() fontSize: CategoryFontSize = 'default'; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
<form name="exerciseFilterForm" (ngSubmit)="applyFilter()"> | ||
<div class="modal-header"> | ||
<h4 class="modal-title"> | ||
<fa-icon [icon]="faFilter" /> | ||
<span jhiTranslate="artemisApp.courseOverview.exerciseFilter.modalTitle"></span> | ||
</h4> | ||
<button type="button" class="btn-close" (click)="closeModal()"></button> | ||
</div> | ||
<div class="modal-body"> | ||
<div class="form-group"> | ||
@if (noFiltersAvailable) { | ||
<div> | ||
<span jhiTranslate="artemisApp.courseOverview.exerciseFilter.noFilterAvailable"></span> | ||
</div> | ||
} | ||
|
||
@if (selectableCategoryOptions.length || selectedCategoryOptions.length) { | ||
<label for="exercise-categories-filter-selection" class="form-control-label h6" jhiTranslate="artemisApp.exercise.categories"></label> | ||
|
||
<input | ||
id="exercise-categories-filter-selection" | ||
type="text" | ||
class="form-control mb-2" | ||
name="category-filter-selection" | ||
[(ngModel)]="model" | ||
[ngbTypeahead]="search" | ||
[placeholder]=" | ||
(!selectableCategoryOptions.length ? 'artemisApp.courseOverview.exerciseFilter.noMoreOptions' : 'artemisApp.exercise.selectCategories') | artemisTranslate | ||
" | ||
(focus)="focus$.next($any($event).target.value)" | ||
(click)="click$.next($any($event).target.value)" | ||
#categoriesFilterSelection="ngbTypeahead" | ||
(selectItem)="onSelectItem($event)" | ||
[resultFormatter]="resultFormatter" | ||
(keydown.enter)="onSelectItem($event)" | ||
[disabled]="!selectableCategoryOptions.length" | ||
/> | ||
|
||
<div class="row"> | ||
<div class="d-flex flex-row flex-wrap"> | ||
@for (categoryFilterOption of selectedCategoryOptions; track categoryFilterOption) { | ||
<div class="p-1"> | ||
<jhi-custom-exercise-category-badge | ||
[category]="categoryFilterOption.category" | ||
[displayRemoveButton]="true" | ||
[onClick]="removeItem(categoryFilterOption)" | ||
[fontSize]="'small'" | ||
/> | ||
</div> | ||
} | ||
</div> | ||
</div> | ||
} | ||
</div> | ||
|
||
@if (typeFilter?.isDisplayed) { | ||
<div class="form-group"> | ||
<label for="exercise-type-filter-selection" class="form-control-label h6" jhiTranslate="artemisApp.ratingList.exerciseType"></label> | ||
<div id="exercise-type-filter-selection"> | ||
@for (typeFilter of typeFilter!.options; track typeFilter) { | ||
<div class="form-check form-check-inline no-left-margin-padding"> | ||
<label class="pointer"> | ||
<input type="checkbox" [(ngModel)]="typeFilter.checked" [name]="typeFilter.value" class="pointer" /> | ||
<fa-icon [icon]="typeFilter.icon" class="ms-2" /> | ||
<span [jhiTranslate]="typeFilter.name" class="ms-1"></span> | ||
</label> | ||
</div> | ||
} | ||
</div> | ||
</div> | ||
} | ||
|
||
<!-- <div class="form-group">--> | ||
<!-- <label class="form-control-label h6" jhiTranslate="artemisApp.courseOverview.exerciseFilter.dueDateRange"> </label>--> | ||
<!-- TODO: Implement Date Range Picker (follow up PR @FlorianGlombik)--> | ||
<!-- </div>--> | ||
|
||
<!-- On typescript version 5.5.4 the explicit check for difficultyFilter is needed, | ||
as otherwise typescript thinks it could be undefined in the loop (the client would not start in this case) --> | ||
@if (difficultyFilter?.isDisplayed && difficultyFilter) { | ||
<div class="form-group"> | ||
<label for="difficulty-filter-selection" class="form-control-label h6" jhiTranslate="artemisApp.exercise.difficulty"></label> | ||
<div id="difficulty-filter-selection"> | ||
@for (difficultyFilterOption of difficultyFilter.options; track difficultyFilterOption) { | ||
<div class="form-check form-check-inline no-left-margin-padding"> | ||
<label class="pointer"> | ||
<input type="checkbox" [(ngModel)]="difficultyFilterOption.checked" [name]="difficultyFilterOption.value" class="pointer" /> | ||
<span [jhiTranslate]="difficultyFilterOption.name" class="ms-1"></span> | ||
</label> | ||
</div> | ||
} | ||
</div> | ||
</div> | ||
} | ||
|
||
<!-- On typescript version 5.5.4 the explicit check for achievedScore is needed, | ||
as otherwise typescript thinks achievedScore could be undefined (the client would not start in this case) --> | ||
@if (achievedScore && achievedScore?.isDisplayed) { | ||
<div class="form-group"> | ||
<label class="form-control-label h6" for="achieved-score-range-slider" jhiTranslate="artemisApp.courseOverview.exerciseFilter.achievedScore"></label> | ||
<jhi-range-slider | ||
id="achieved-score-range-slider" | ||
[generalMinValue]="achievedScore.filter.generalMin" | ||
[generalMaxValue]="achievedScore.filter.generalMax" | ||
[(selectedMinValue)]="achievedScore.filter.selectedMin" | ||
[(selectedMaxValue)]="achievedScore.filter.selectedMax" | ||
[step]="achievedScore.filter.step" | ||
[labelSymbol]="'%'" | ||
/> | ||
</div> | ||
} | ||
|
||
<!-- On typescript version 5.5.4 the explicit check for achievablePoints is needed, | ||
as otherwise typescript thinks achievablePoints could be undefined (the client would not start in this case) --> | ||
@if (achievablePoints && achievablePoints.isDisplayed) { | ||
<div class="form-group"> | ||
<label class="form-control-label h6" for="achieved-points-range-slider" jhiTranslate="artemisApp.courseOverview.exerciseFilter.achievablePoints"></label> | ||
<jhi-range-slider | ||
id="achieved-points-range-slider" | ||
[generalMinValue]="achievablePoints.filter.generalMin" | ||
[generalMaxValue]="achievablePoints.filter.generalMax" | ||
[(selectedMinValue)]="achievablePoints.filter.selectedMin" | ||
[(selectedMaxValue)]="achievablePoints.filter.selectedMax" | ||
[step]="achievablePoints.filter.step" | ||
/> | ||
</div> | ||
} | ||
</div> | ||
<div class="modal-footer"> | ||
<button type="button" class="btn btn-outline-secondary" (click)="clearFilter()"> | ||
<span jhiTranslate="artemisApp.courseOverview.exerciseFilter.clearFilter"></span> | ||
</button> | ||
|
||
<div class="ms-auto"> | ||
<button type="button" class="btn btn-secondary" (click)="closeModal()" jhiTranslate="entity.action.cancel" aria-label="Close"></button> | ||
<button | ||
type="button" | ||
class="btn btn-primary" | ||
(click)="applyFilter()" | ||
jhiTranslate="artemisApp.courseOverview.exerciseFilter.applyFilter" | ||
[disabled]="noFiltersAvailable" | ||
></button> | ||
</div> | ||
</div> | ||
</form> |
20 changes: 20 additions & 0 deletions
20
src/main/webapp/app/shared/exercise-filter/exercise-filter-modal.component.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
ensures that the category dropdown selection does not overflow the screen to the bottom | ||
(instead a scrollbar will be displayed) | ||
*/ | ||
:host ::ng-deep ngb-typeahead-window.dropdown-menu { | ||
max-height: 25rem; | ||
overflow-y: auto; | ||
} | ||
|
||
/* align the first checkbox to the left */ | ||
.no-left-margin-padding:first-child { | ||
margin-left: 0; | ||
padding-left: 0; | ||
} | ||
|
||
/* otherwise the dropdown changes the color on hover if no further options can be selected */ | ||
.form-control:disabled, | ||
.form-control[disabled] { | ||
pointer-events: none; | ||
} |
Oops, something went wrong.