add new changes
This commit is contained in:
@@ -11,6 +11,14 @@ export interface Question {
|
||||
difficulty: Difficulty;
|
||||
categoryId: string;
|
||||
categoryName?: string;
|
||||
category?: {
|
||||
id: string;
|
||||
name: string;
|
||||
slug?: string;
|
||||
icon?: string;
|
||||
color?: string;
|
||||
guestAccessible?: boolean;
|
||||
};
|
||||
options?: string[]; // For multiple choice
|
||||
correctAnswer: string | string[];
|
||||
explanation: string;
|
||||
|
||||
@@ -747,6 +747,46 @@ export class AdminService {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all questions with pagination, search, and filtering
|
||||
* Endpoint: GET /api/admin/questions
|
||||
*/
|
||||
getAllQuestions(params: {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
search?: string;
|
||||
category?: string;
|
||||
difficulty?: string;
|
||||
sortBy?: string;
|
||||
order?: string;
|
||||
}): Observable<{
|
||||
success: boolean;
|
||||
count: number;
|
||||
total: number;
|
||||
page: number;
|
||||
totalPages: number;
|
||||
limit: number;
|
||||
filters: any;
|
||||
data: Question[];
|
||||
message: string;
|
||||
}> {
|
||||
let queryParams: any = {};
|
||||
|
||||
if (params.page) queryParams.page = params.page;
|
||||
if (params.limit) queryParams.limit = params.limit;
|
||||
if (params.search) queryParams.search = params.search;
|
||||
if (params.category && params.category !== 'all') queryParams.category = params.category;
|
||||
if (params.difficulty && params.difficulty !== 'all') queryParams.difficulty = params.difficulty;
|
||||
if (params.sortBy) queryParams.sortBy = params.sortBy;
|
||||
if (params.order) queryParams.order = params.order.toUpperCase();
|
||||
|
||||
return this.http.get<any>(`${this.apiUrl}/questions`, { params: queryParams }).pipe(
|
||||
catchError((error: HttpErrorResponse) => this.handleQuestionError(error, 'Failed to load questions'))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Delete question (soft delete)
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,6 @@ import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { COMMA, ENTER } from '@angular/cdk/keycodes';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { AdminService } from '../../../core/services/admin.service';
|
||||
import { CategoryService } from '../../../core/services/category.service';
|
||||
import { Question, QuestionFormData } from '../../../core/models/question.model';
|
||||
@@ -123,23 +122,22 @@ export class AdminQuestionFormComponent implements OnInit {
|
||||
this.categoryService.getCategories().subscribe();
|
||||
|
||||
// Check if we're in edit mode
|
||||
this.route.params
|
||||
.pipe(takeUntilDestroyed())
|
||||
.subscribe(params => {
|
||||
const id = params['id'];
|
||||
if (id) {
|
||||
this.route.params.subscribe(params => {
|
||||
const id = params['id'];
|
||||
if (id) {
|
||||
// Defer signal updates to avoid ExpressionChangedAfterItHasBeenCheckedError
|
||||
setTimeout(() => {
|
||||
this.isEditMode.set(true);
|
||||
this.questionId.set(id);
|
||||
this.loadQuestion(id);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Watch for question type changes
|
||||
this.questionForm.get('questionType')?.valueChanges
|
||||
.pipe(takeUntilDestroyed())
|
||||
.subscribe((type: QuestionType) => {
|
||||
this.onQuestionTypeChange(type);
|
||||
});
|
||||
this.questionForm.get('questionType')?.valueChanges.subscribe((type: QuestionType) => {
|
||||
this.onQuestionTypeChange(type);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,9 +146,7 @@ export class AdminQuestionFormComponent implements OnInit {
|
||||
private loadQuestion(id: string): void {
|
||||
this.isLoadingQuestion.set(true);
|
||||
|
||||
this.adminService.getQuestion(id)
|
||||
.pipe(takeUntilDestroyed())
|
||||
.subscribe({
|
||||
this.adminService.getQuestion(id).subscribe({
|
||||
next: (response) => {
|
||||
this.isLoadingQuestion.set(false);
|
||||
this.populateForm(response.data);
|
||||
@@ -391,9 +387,7 @@ export class AdminQuestionFormComponent implements OnInit {
|
||||
? this.adminService.updateQuestion(this.questionId()!, questionData)
|
||||
: this.adminService.createQuestion(questionData);
|
||||
|
||||
serviceCall
|
||||
.pipe(takeUntilDestroyed())
|
||||
.subscribe({
|
||||
serviceCall.subscribe({
|
||||
next: (response) => {
|
||||
this.isSubmitting.set(false);
|
||||
this.router.navigate(['/admin/questions']);
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
<ng-container matColumnDef="category">
|
||||
<th mat-header-cell *matHeaderCellDef>Category</th>
|
||||
<td mat-cell *matCellDef="let question">
|
||||
{{ getCategoryName(question.categoryId) }}
|
||||
{{ getCategoryName(question) }}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
|
||||
@@ -177,23 +177,30 @@ export class AdminQuestionsComponent implements OnInit {
|
||||
page: this.currentPage(),
|
||||
limit: this.pageSize(),
|
||||
search: filters.search || undefined,
|
||||
categoryId: filters.category !== 'all' ? filters.category : undefined,
|
||||
category: filters.category !== 'all' ? filters.category : undefined,
|
||||
difficulty: filters.difficulty !== 'all' ? filters.difficulty : undefined,
|
||||
questionType: filters.type !== 'all' ? filters.type : undefined,
|
||||
sortBy: filters.sortBy,
|
||||
sortOrder: filters.sortOrder
|
||||
order: filters.sortOrder
|
||||
};
|
||||
|
||||
// Remove undefined values
|
||||
Object.keys(params).forEach(key => params[key] === undefined && delete params[key]);
|
||||
|
||||
// TODO: Replace with actual API call when available
|
||||
// For now, using mock data
|
||||
setTimeout(() => {
|
||||
this.questions.set([]);
|
||||
this.totalQuestions.set(0);
|
||||
this.isLoading.set(false);
|
||||
}, 500);
|
||||
this.adminService.getAllQuestions(params)
|
||||
.pipe(finalize(() => this.isLoading.set(false)))
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
this.questions.set(response.data);
|
||||
this.totalQuestions.set(response.total);
|
||||
this.currentPage.set(response.page);
|
||||
},
|
||||
error: (error) => {
|
||||
this.error.set(error.message || 'Failed to load questions');
|
||||
this.questions.set([]);
|
||||
this.totalQuestions.set(0);
|
||||
console.error('Load questions error:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,11 +260,27 @@ export class AdminQuestionsComponent implements OnInit {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get category name by ID
|
||||
* Get category name from question
|
||||
* The API returns a nested category object with the question
|
||||
*/
|
||||
getCategoryName(categoryId: string | number): string {
|
||||
const category = this.categories().find(c => c.id === categoryId || c.id === categoryId.toString());
|
||||
return category?.name || 'Unknown';
|
||||
getCategoryName(question: Question): string {
|
||||
// First try to get from nested category object (API response)
|
||||
if (question.category?.name) {
|
||||
return question.category.name;
|
||||
}
|
||||
|
||||
// Fallback: try to find by categoryId in loaded categories
|
||||
if (question.categoryId) {
|
||||
const category = this.categories().find(
|
||||
c => c.id === question.categoryId || c.id === question.categoryId.toString()
|
||||
);
|
||||
if (category) {
|
||||
return category.name;
|
||||
}
|
||||
}
|
||||
|
||||
// Last fallback: use categoryName property if available
|
||||
return question.categoryName || 'Unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user