Skip to content

Commit 0db5a22

Browse files
authored
Merge pull request #30 from CS3219-AY2425S1/question-spa-frontend
Implement Question SPA
2 parents 363d91d + 28dbdd2 commit 0db5a22

23 files changed

+851
-6
lines changed

frontend/angular.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
"budgets": [
3737
{
3838
"type": "initial",
39-
"maximumWarning": "500kB",
40-
"maximumError": "1MB"
39+
"maximumWarning": "2MB",
40+
"maximumError": "3MB"
4141
},
4242
{
4343
"type": "anyComponentStyle",

frontend/package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@angular/platform-browser-dynamic": "^18.2.0",
2222
"@angular/router": "^18.2.0",
2323
"primeflex": "^3.3.1",
24+
"primeicons": "^7.0.0",
2425
"primeng": "^17.18.10",
2526
"rxjs": "~7.8.0",
2627
"tslib": "^2.3.0",
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { QuestionService } from './question.service';
4+
5+
describe('QuestionService', () => {
6+
let service: QuestionService;
7+
8+
beforeEach(() => {
9+
TestBed.configureTestingModule({});
10+
service = TestBed.inject(QuestionService);
11+
});
12+
13+
it('should be created', () => {
14+
expect(service).toBeTruthy();
15+
});
16+
});
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams } from '@angular/common/http';
2+
import { Injectable } from '@angular/core';
3+
import { API_CONFIG } from '../app/api.config';
4+
import { catchError, Observable, throwError } from 'rxjs';
5+
import { SingleQuestionResponse, QuestionResponse, QuestionBody } from '../app/questions/question.model';
6+
import { TopicResponse } from '../app/questions/topic.model';
7+
8+
@Injectable({
9+
providedIn: 'root',
10+
})
11+
export class QuestionService {
12+
private baseUrl = API_CONFIG.baseUrl;
13+
14+
private httpOptions = {
15+
headers: new HttpHeaders({
16+
'Content-Type': 'application/json',
17+
}),
18+
};
19+
20+
constructor(private http: HttpClient) {}
21+
22+
getQuestions(
23+
title?: string,
24+
description?: string,
25+
topics?: string[],
26+
difficulty?: string,
27+
): Observable<QuestionResponse> {
28+
let params = new HttpParams();
29+
30+
if (title) {
31+
params = params.append('title', title);
32+
}
33+
if (description) {
34+
params = params.append('description', description);
35+
}
36+
if (topics && topics.length > 0) {
37+
params = params.append('topics', topics.join(','));
38+
}
39+
if (difficulty) {
40+
params = params.append('difficulty', difficulty);
41+
}
42+
43+
// send request
44+
return this.http.get<QuestionResponse>(this.baseUrl + '/questions', { params });
45+
}
46+
47+
getQuestionByID(id: number): Observable<QuestionResponse> {
48+
return this.http.get<QuestionResponse>(this.baseUrl + '/questions/' + id);
49+
}
50+
51+
getQuestionByParam(topics: string[], difficulty: string, limit?: number): Observable<QuestionResponse> {
52+
let params = new HttpParams();
53+
54+
if (limit) {
55+
params = params.append('limit', limit);
56+
}
57+
params = params.append('topics', topics.join(',')).append('difficulty', difficulty);
58+
59+
return this.http.get<QuestionResponse>(this.baseUrl + '/questions/search', { params });
60+
}
61+
62+
getTopics(): Observable<TopicResponse> {
63+
return this.http.get<TopicResponse>(this.baseUrl + '/questions/topics');
64+
}
65+
66+
addQuestion(question: QuestionBody): Observable<SingleQuestionResponse> {
67+
return this.http
68+
.post<SingleQuestionResponse>(this.baseUrl + '/questions', question, this.httpOptions)
69+
.pipe(catchError(this.handleError));
70+
}
71+
72+
updateQuestion(id: number, question: QuestionBody): Observable<SingleQuestionResponse> {
73+
return this.http
74+
.put<SingleQuestionResponse>(this.baseUrl + '/questions/' + id, question, this.httpOptions)
75+
.pipe(catchError(this.handleError));
76+
}
77+
78+
deleteQuestion(id: number): Observable<SingleQuestionResponse> {
79+
return this.http
80+
.delete<SingleQuestionResponse>(this.baseUrl + '/questions/' + id)
81+
.pipe(catchError(this.handleError));
82+
}
83+
84+
handleError(error: HttpErrorResponse) {
85+
return throwError(error);
86+
}
87+
}

frontend/src/app/api.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const API_CONFIG = {
2+
baseUrl: 'http://localhost:8081',
3+
};

frontend/src/app/app.config.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
22
import { provideRouter } from '@angular/router';
3-
43
import { routes } from './app.routes';
54
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
5+
import { provideHttpClient } from '@angular/common/http';
66

77
export const appConfig: ApplicationConfig = {
8-
providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideAnimationsAsync()],
8+
providers: [
9+
provideZoneChangeDetection({ eventCoalescing: true }),
10+
provideRouter(routes),
11+
provideAnimationsAsync(),
12+
provideHttpClient(),
13+
],
914
};

frontend/src/app/app.routes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Routes } from '@angular/router';
2+
import { QuestionsComponent } from './questions/questions.component';
23

34
const accountModule = () => import('./account/account.module').then(x => x.AccountModule);
45

@@ -7,4 +8,8 @@ export const routes: Routes = [
78
path: 'account',
89
loadChildren: accountModule,
910
},
11+
{
12+
path: 'questions',
13+
component: QuestionsComponent,
14+
},
1015
];
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface Column {
2+
field: string;
3+
header: string;
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export enum DifficultyLevels {
2+
EASY = 'Easy',
3+
MEDIUM = 'Medium',
4+
HARD = 'Hard',
5+
}

0 commit comments

Comments
 (0)