Skip to content

Commit

Permalink
Merge pull request #24 from CS3219-AY2425S1/frontend/question-descrip…
Browse files Browse the repository at this point in the history
…tion

DRAFT - Sort and Filter Feature
  • Loading branch information
kervyntan authored Sep 29, 2024
2 parents 53fd0b5 + 3f610ff commit 43c3b42
Show file tree
Hide file tree
Showing 12 changed files with 254 additions and 47 deletions.
8 changes: 8 additions & 0 deletions peer-prep-fe/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions peer-prep-fe/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@angular/cli": "^18.2.4",
"@angular/compiler-cli": "^18.2.0",
"@types/jasmine": "~5.1.0",
"@types/mocha": "^10.0.8",
"jasmine-core": "~5.2.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
Expand Down
4 changes: 4 additions & 0 deletions peer-prep-fe/src/app/models/category.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Category {
category_id: string,
category_name: string
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div>
<h1 class="tourney-title">List of Questions</h1>
<app-search-and-filter (refresh)="refreshQuestions()" (sort)="refreshQuestions(true)" (search)="refreshQuestionsSearch($event)"></app-search-and-filter>
<app-search-and-filter (refresh)="refreshQuestions()" (filter)="applyFilter($event)" (sort)="refreshQuestions(true)" (search)="refreshQuestionsSearch($event)"></app-search-and-filter>
<div class="question-list">
<ul>
<li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,51 @@ import {HttpClientModule} from "@angular/common/http";
export class QuestionListComponent implements OnInit {

questions: Question[] = [];
// filteredQuestions: Question[] = [];

constructor(private questionService : QuestionService) { }

ngOnInit(): void {
this.loadQuestions();
}

loadQuestions(hasSort?: boolean) {
if (hasSort) {
// default alphabetical sorting for now
this.questionService.getAllQuestionSorted("question_title", "asc").subscribe((data: any) => {
this.questions = data.data.data;
}, (error) => {
console.error('Error fetching: ', error);
})
} else {
// gets default ordering of questions
this.questionService.getAllQuestion().subscribe((data: any) => {
this.questions = data.data.data;
}, (error) => {
console.error('Error fetching: ', error);
})
}
loadQuestions() {
// gets default ordering of questions
this.questionService.getAllQuestion().subscribe((data: any) => {
this.questions = data.data.data;
}, (error) => {
console.error('Error fetching: ', error);
})
}

loadSortedQuestions() {
this.questionService.getAllQuestionSorted("question_title", "asc").subscribe((data: any) => {
this.questions = data.data.data;
}, (error) => {
console.error('Error fetching: ', error);
})
}

loadFilteredQuestions(filterBy?: string, filterValues?: string) {
this.questionService.getFilteredQuestions(filterBy, filterValues).subscribe((data: any) => {
this.questions = data.data.data;
// this.filteredQuestions = data.data.data;
}, (error) => {
console.error('Error fetching: ', error);
})
}

applyFilter(event: { filterBy: string, filterValues: string} ) {
this.loadFilteredQuestions(event.filterBy, event.filterValues);
console.log('Filtering by:', event.filterBy, event.filterValues);
}

refreshQuestions(hasSort?: boolean) {
this.loadQuestions(hasSort);
if (hasSort) {
this.loadSortedQuestions();
} else {
this.loadQuestions();
}
}

loadSearchedQuestions(searchTerm?: string) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
.container {
/* Remove flex from all elements */
* {
box-sizing: border-box; /* Instead of display: flex */
}

/* Fix flex layout only for specific elements */
.container {
display: flex;
align-items: center;
}
Expand All @@ -13,62 +19,98 @@
font-size: 22px;
background-color: transparent;
border: 1px solid white;
padding: 3px 40px 3px 40px;
padding: 2px 40px 6px 40px;
margin-right: 10px;
/* margin-left: 10px; */
border-radius: 10px;
width: 400px;
text-transform: uppercase;
}

.search-button {
background-color: transparent;
border-radius: 10px;
}

.search-icon {
/* position: absolute; */
/* top: 50%; */
/* transform: translateY(-50%); */
color: white;
/* color: white; */
background-color: transparent;
pointer-events: none;
}

button[type="submit"] {
/* text-indent: -999px; */
overflow: hidden;
color: white;
width: 40px;
height: 40px;
padding: 0;
margin: 0;
/* border: 1px solid transparent; */
border-radius: inherit;
border-radius: 10px;
cursor: pointer;
opacity: 0.7;
opacity: 1;
}

button[type="submit"]:hover {
opacity: 1;
}

.button-container {
display: flex;
margin-left: 20px;
display: flex;
margin-left: 20px;
position: relative;
}

.dropdown {
position: relative;
}

.dropdown-content {
display: none;
position: absolute;
background-color: white;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
top: 100%; /* Position it below the filter button */
left: 0;
}

.filter-container:hover .dropdown-content {
display: block;
}

/* Styling for buttons inside dropdowns */
.dropdown-content button {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
background-color: #f9f9f9;
width: 100%;
}

.dropdown-content button:hover {
background-color: #ddd;
}

/* Button styling */
#button {
background-color: #003D84;
color: white;
color: white;
font-family: "Signika Negative", sans-serif;
font-size: 20px;
border-radius: 10px;
padding: 6px 15px;
padding: 6px 15px;
margin-right: 8px;
border: none;
cursor: pointer;
}

/*
#button:last-child {
margin-right: 0;
}

* {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}

} */
Original file line number Diff line number Diff line change
@@ -1,13 +1,49 @@
<div class="container">
<div class="search-container">
<input class="search-bar" type="search" placeholder="Search..." [(ngModel)]="searchTerm" />
<button type="submit" (click)="searchQuestions()">
<button type="submit" class="search-button" (click)="searchQuestions()">
<i class="fa-solid fa-magnifying-glass search-icon"> </i>
</button>
</div>

<div class="button-container">
<button id="button" (click)="sortQuestions()">SORT</button>
<button id="button">FILTER</button>
<button id="button" (click)="openAddModal()" >ADD QUESTION</button>
<button id="button" (click)="sortQuestions()">SORT</button>

<!-- Filter Button with Hover -->
<div class="filter-container">
<button id="button" (mouseover)="showFilterOptions = true">
FILTER
</button>

<!-- Dropdown Content to toggle between Difficulty and Category buttons, or options -->
<div class="dropdown-content" *ngIf="showFilterOptions" (mouseleave)="showFilterOptions = false">

<!-- Show Difficulty and Category buttons if no filter option is selected -->
<div *ngIf="!isDifficultyClicked && !isCategoryClicked">
<button class="dropdown-btn" (click)="showDifficultyOptions()">Difficulty</button>
<button class="dropdown-btn" (click)="showCategoryOptions()">Category</button>
</div>

<!-- Show Difficulty Options -->
<div *ngIf="isDifficultyClicked">
<button (click)="filterByDifficulty('Easy')">Easy</button>
<button (click)="filterByDifficulty('Medium')">Medium</button>
<button (click)="filterByDifficulty('Hard')">Hard</button>
</div>

<!-- Show Category Options -->
<div *ngIf="isCategoryClicked">
<button *ngFor="let category of categories"
(click)="filterByCategory(category)"
>
{{ category}}
</button>
</div>

</div>
</div>

<button id="button" (click)="openAddModal()">ADD QUESTION</button>
</div>
</div>
</div>

Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { SearchAndFilterComponent } from './search-and-filter.component';
import { HttpClientTestingModule } from '@angular/common/http/testing'; // Import this to handle HttpClient
import { ReactiveFormsModule } from '@angular/forms';

describe('SearchAndFilterComponent', () => {
let component: SearchAndFilterComponent;
let fixture: ComponentFixture<SearchAndFilterComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SearchAndFilterComponent]
imports: [HttpClientTestingModule, ReactiveFormsModule, SearchAndFilterComponent]
})
.compileComponents();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,74 @@
import {Component, EventEmitter, Output} from '@angular/core';
import {EditPageComponent} from "../../edit-page/edit-page.component";
import { CommonModule } from '@angular/common';
import { QuestionService } from '../../services/question.service';
import {MatDialog} from "@angular/material/dialog";
import {AddPageComponent} from "../../add-page/add-page.component";
import { QuestionService } from '../../services/question.service';
import { Category } from '../../app/models/category.model';
import { FormsModule } from '@angular/forms';

@Component({
selector: 'app-search-and-filter',
standalone: true,
imports: [FormsModule],
imports: [CommonModule, FormsModule],
templateUrl: './search-and-filter.component.html',
styleUrl: './search-and-filter.component.css'
})
export class SearchAndFilterComponent {
@Output() refresh = new EventEmitter<void>();
@Output() sort = new EventEmitter<void>(); // event for when "SORT" button is clicked
@Output() filter = new EventEmitter<{ filterBy: string, filterValues: string }>();
@Output() search = new EventEmitter<string>();

searchTerm: string = ''; // Declare the searchTerm property

constructor(private dialog: MatDialog) {}
categories: string[] = [];
showFilterOptions = false; // Controls showing filter options
isDifficultyClicked = false;
isCategoryClicked = false;

constructor(private dialog: MatDialog, private questionService: QuestionService) {}

ngOnInit(): void {
this.questionService.getQuestionCategories().subscribe(
(response: any) => {
console.log('Category API response:', response);
// Map the response to Category array
this.categories = response.data.data;
},
(error) => {
console.error('Error fetching categories:', error);
}
);
}

// Show difficulty options
showDifficultyOptions() {
this.isDifficultyClicked = true;
this.isCategoryClicked = false;
}

// Show category options
showCategoryOptions() {
this.isCategoryClicked = true;
this.isDifficultyClicked = false;
}

// Emit an event to filter by difficulty
filterByDifficulty(difficulty: string) {
console.log('Filtering by difficulty:', difficulty);
this.filter.emit({ filterBy: 'question_complexity', filterValues: difficulty });
this.isDifficultyClicked = false;
this.showFilterOptions = false;
}

// Emit an event to filter by category
filterByCategory(category: string) {
console.log('Filtering by category:', category);
this.filter.emit({ filterBy: 'question_categories', filterValues: category });
this.isCategoryClicked = false;
this.showFilterOptions = false;
}

sortQuestions() {
console.log('Sort button clicked');
Expand Down
Loading

0 comments on commit 43c3b42

Please sign in to comment.