Skip to content

Commit abd4117

Browse files
committed
Integrate API calls with UI
1 parent bbbf308 commit abd4117

File tree

2 files changed

+177
-107
lines changed

2 files changed

+177
-107
lines changed

frontend/src/app/questions/question.model.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export interface SingleQuestionResponse {
1111
}
1212

1313
export interface Question {
14-
_id?: string;
1514
id: number;
1615
description: string;
1716
difficulty: string;

frontend/src/app/questions/questions.component.ts

Lines changed: 177 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { DialogModule } from 'primeng/dialog';
1111
import { MultiSelectModule } from 'primeng/multiselect';
1212
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
1313
import { DropdownModule } from 'primeng/dropdown';
14-
import { Question } from './question.model';
14+
import { Question, SingleQuestionResponse, QuestionBody } from './question.model';
1515
import { Column } from './column.model';
1616
import { Topic } from './topic.model';
1717
import { Difficulty } from './difficulty.model';
@@ -70,76 +70,16 @@ export class QuestionsComponent implements OnInit {
7070

7171
ngOnInit() {
7272
// two way binding for forms is not working for some reason unless question is initialised with empty values
73-
this.question = {
74-
_id: '',
75-
id: -1,
76-
title: '',
77-
topics: [],
78-
description: '',
79-
difficulty: '',
80-
};
81-
82-
forkJoin({
83-
questions: this.questionService.getQuestions(),
84-
topics: this.questionService.getTopics(),
85-
}).subscribe({
86-
next: results => {
87-
this.questions = results.questions.data || [];
88-
this.topics =
89-
results.topics.data?.map(topic => ({
90-
label: topic,
91-
value: topic,
92-
})) || [];
93-
},
94-
error: (error: Error) => {
95-
console.error(error);
96-
this.questions = [];
97-
this.topics = [];
98-
this.messageService.add({
99-
severity: 'error',
100-
summary: 'Error',
101-
detail: 'Failed to load data. Please try again later.',
102-
life: 3000,
103-
});
104-
},
105-
complete: () => {
106-
// TODO: add loading state for this
107-
console.log('complete');
108-
},
109-
});
73+
this.initQuestion();
11074

111-
this.difficulties = [
112-
{ label: DifficultyLevels.EASY, value: DifficultyLevels.EASY },
113-
{ label: DifficultyLevels.MEDIUM, value: DifficultyLevels.MEDIUM },
114-
{ label: DifficultyLevels.HARD, value: DifficultyLevels.HARD },
115-
];
75+
// fetch data from API call
76+
this.handleInitData();
11677

117-
this.questionFormGroup = new FormGroup({
118-
selectedTopics: new FormControl<string[] | null>([]),
119-
selectedDifficulty: new FormControl<Difficulty[] | null>([]),
120-
textTitle: new FormControl<string | null>(''),
121-
textDescription: new FormControl<string | null>(''),
122-
});
123-
124-
// Dropdown difficulty listener
125-
this.questionFormGroup.get('selectedDifficulty')?.valueChanges.subscribe(v => {
126-
this.question.difficulty = v;
127-
});
128-
129-
// Multiselect topics listener
130-
this.questionFormGroup.get('selectedTopics')?.valueChanges.subscribe(v => {
131-
this.question.topics = v;
132-
});
78+
this.initDifficulties();
13379

134-
// text title listener
135-
this.questionFormGroup.get('textTitle')?.valueChanges.subscribe(v => {
136-
this.question.title = v;
137-
});
80+
this.initFormGroup();
13881

139-
// text description listener
140-
this.questionFormGroup.get('textDescription')?.valueChanges.subscribe(v => {
141-
this.question.description = v;
142-
});
82+
this.initListeners();
14383
}
14484

14585
openNewQuestion() {
@@ -155,14 +95,7 @@ export class QuestionsComponent implements OnInit {
15595
header: 'Delete Confirmation',
15696
icon: 'pi pi-exclamation-triangle',
15797
accept: () => {
158-
this.questions = this.questions?.filter(val => !this.selectedQuestions?.includes(val));
159-
this.selectedQuestions = null;
160-
this.messageService.add({
161-
severity: 'success',
162-
summary: 'Successful',
163-
detail: 'Question(s) Deleted',
164-
life: 3000,
165-
});
98+
this.handleDeleteQuestionResponse();
16699
},
167100
});
168101
}
@@ -181,36 +114,26 @@ export class QuestionsComponent implements OnInit {
181114

182115
if (this.question.id) {
183116
// update
184-
if (this.questions) {
185-
this.questions[this.questions.findIndex(x => x.id == this.question.id)] = this.question;
186-
}
187-
188-
this.messageService.add({
189-
severity: 'success',
190-
summary: 'Successful',
191-
detail: 'Question has been updated successfully',
192-
life: 3000,
193-
});
117+
const { id, ...questionBody } = this.question;
118+
this.handleEditQuestionResponse(id, questionBody);
194119
} else {
195120
// add
196-
this.question.id = this.createId();
197-
198-
if (this.questions) {
199-
this.questions = [...this.questions, this.question];
200-
}
201-
202-
this.messageService.add({
203-
severity: 'success',
204-
summary: 'Successful',
205-
detail: 'New Question Added',
206-
life: 3000,
207-
});
121+
this.handleAddQuestionResponse();
208122
}
209123

210124
this.isDialogVisible = false;
211125
this.question = {} as Question;
212126
}
213127

128+
resetFormGroup() {
129+
this.questionFormGroup.reset({
130+
selectedTopics: [],
131+
selectedDifficulty: '',
132+
textTitle: '',
133+
textDescription: '',
134+
});
135+
}
136+
214137
editQuestion(question: Question) {
215138
this.question.id = question.id;
216139
this.questionFormGroup.patchValue({
@@ -222,17 +145,165 @@ export class QuestionsComponent implements OnInit {
222145
this.isDialogVisible = true;
223146
}
224147

225-
// assuming newest question is always appended at the back
226-
createId() {
227-
return this.questions ? Number(this.questions.at(-1)?.id) + 1 : -1;
148+
initListeners() {
149+
// Dropdown difficulty listener
150+
this.questionFormGroup.get('selectedDifficulty')?.valueChanges.subscribe(v => {
151+
this.question.difficulty = v;
152+
});
153+
154+
// Multiselect topics listener
155+
this.questionFormGroup.get('selectedTopics')?.valueChanges.subscribe(v => {
156+
this.question.topics = v;
157+
});
158+
159+
// text title listener
160+
this.questionFormGroup.get('textTitle')?.valueChanges.subscribe(v => {
161+
this.question.title = v;
162+
});
163+
164+
// text description listener
165+
this.questionFormGroup.get('textDescription')?.valueChanges.subscribe(v => {
166+
this.question.description = v;
167+
});
228168
}
229169

230-
resetFormGroup() {
231-
this.questionFormGroup.reset({
232-
selectedTopics: [],
233-
selectedDifficulty: '',
234-
textTitle: '',
235-
textDescription: '',
170+
initFormGroup() {
171+
this.questionFormGroup = new FormGroup({
172+
selectedTopics: new FormControl<string[] | null>([]),
173+
selectedDifficulty: new FormControl<Difficulty[] | null>([]),
174+
textTitle: new FormControl<string | null>(''),
175+
textDescription: new FormControl<string | null>(''),
176+
});
177+
}
178+
179+
initDifficulties() {
180+
this.difficulties = [
181+
{ label: DifficultyLevels.EASY, value: DifficultyLevels.EASY },
182+
{ label: DifficultyLevels.MEDIUM, value: DifficultyLevels.MEDIUM },
183+
{ label: DifficultyLevels.HARD, value: DifficultyLevels.HARD },
184+
];
185+
}
186+
187+
initQuestion() {
188+
this.question = {
189+
id: -1,
190+
title: '',
191+
topics: [],
192+
description: '',
193+
difficulty: '',
194+
};
195+
}
196+
197+
handleAddQuestionResponse() {
198+
this.questionService.addQuestion(this.question).subscribe({
199+
next: (response: SingleQuestionResponse) => {
200+
if (this.questions) {
201+
this.questions = [...this.questions, response.data];
202+
}
203+
},
204+
error: (error: Error) => {
205+
console.log(error);
206+
this.messageService.add({
207+
severity: 'error',
208+
summary: 'Error',
209+
detail: 'Failed to add new question. Please try again later.',
210+
life: 3000,
211+
});
212+
},
213+
complete: () => {
214+
this.messageService.add({
215+
severity: 'success',
216+
summary: 'Successful',
217+
detail: 'New Question Added',
218+
life: 3000,
219+
});
220+
},
221+
});
222+
}
223+
224+
handleDeleteQuestionResponse() {
225+
const deleteRequests = this.selectedQuestions?.map(q => this.questionService.deleteQuestion(q.id));
226+
227+
forkJoin(deleteRequests!).subscribe({
228+
next: () => {
229+
// delete locally
230+
this.questions = this.questions?.filter(val => !this.selectedQuestions?.includes(val));
231+
this.selectedQuestions = null;
232+
},
233+
error: () => {
234+
// Handle any errors from the forkJoin if necessary
235+
this.messageService.add({
236+
severity: 'error',
237+
summary: 'Error',
238+
detail: 'Some questions could not be deleted. Please try again later.',
239+
life: 3000,
240+
});
241+
},
242+
complete: () => {
243+
this.messageService.add({
244+
severity: 'success',
245+
summary: 'Successful',
246+
detail: 'Question(s) Deleted',
247+
life: 3000,
248+
});
249+
},
250+
});
251+
}
252+
253+
handleEditQuestionResponse(id: number, question: QuestionBody) {
254+
this.questionService.updateQuestion(id, question).subscribe({
255+
next: (response: SingleQuestionResponse) => {
256+
this.questions[this.questions.findIndex(x => x.id == id)] = response.data;
257+
},
258+
error: (error: Error) => {
259+
console.log(error);
260+
console.log(question);
261+
this.messageService.add({
262+
severity: 'error',
263+
summary: 'Error',
264+
detail: 'Failed to edit question. Please try again later.',
265+
life: 3000,
266+
});
267+
},
268+
complete: () => {
269+
this.messageService.add({
270+
severity: 'success',
271+
summary: 'Successful',
272+
detail: 'Question has been updated successfully',
273+
life: 3000,
274+
});
275+
},
276+
});
277+
}
278+
279+
handleInitData() {
280+
forkJoin({
281+
questions: this.questionService.getQuestions(),
282+
topics: this.questionService.getTopics(),
283+
}).subscribe({
284+
next: results => {
285+
this.questions = results.questions.data || [];
286+
this.topics =
287+
results.topics.data?.map(topic => ({
288+
label: topic,
289+
value: topic,
290+
})) || [];
291+
},
292+
error: (error: Error) => {
293+
console.error(error);
294+
this.questions = [];
295+
this.topics = [];
296+
this.messageService.add({
297+
severity: 'error',
298+
summary: 'Error',
299+
detail: 'Failed to load data. Please try again later.',
300+
life: 3000,
301+
});
302+
},
303+
complete: () => {
304+
// TODO: add loading state for this
305+
console.log('complete');
306+
},
236307
});
237308
}
238309
}

0 commit comments

Comments
 (0)