feat: restore learning feedback and knowledge capabilities
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
import type { ExamDetail, ExamResultItem, ExamSubmitResult, ExamSummary, SupervisorExamDraft } from '@/types/api';
|
||||
import { apiRequest } from './api';
|
||||
|
||||
type RawExamQuestion = {
|
||||
id: number;
|
||||
questionType: string;
|
||||
stem: string;
|
||||
options?: string[];
|
||||
correctAnswers?: string[];
|
||||
score: number;
|
||||
explanation?: string;
|
||||
sortOrder?: number;
|
||||
};
|
||||
|
||||
type RawExamSummary = {
|
||||
id: number;
|
||||
title: string;
|
||||
projectCode?: string;
|
||||
positionCode?: string;
|
||||
status: string;
|
||||
totalScore: number;
|
||||
passScore: number;
|
||||
dueDate?: string | null;
|
||||
questionCount: number;
|
||||
targetCount: number;
|
||||
submissionCount: number;
|
||||
bestScore?: number | null;
|
||||
passed?: boolean | null;
|
||||
};
|
||||
|
||||
type RawExamDetail = Omit<RawExamSummary, 'questionCount' | 'targetCount' | 'submissionCount' | 'bestScore' | 'passed'> & {
|
||||
questions: RawExamQuestion[];
|
||||
targets: ExamDetail['targets'];
|
||||
};
|
||||
|
||||
const normalizeSummary = (item: RawExamSummary): ExamSummary => ({
|
||||
...item,
|
||||
passingScore: item.passScore
|
||||
});
|
||||
|
||||
const normalizeDetail = (item: RawExamDetail): ExamDetail => ({
|
||||
...item,
|
||||
passingScore: item.passScore,
|
||||
questionCount: item.questions?.length || 0,
|
||||
targetCount: item.targets?.length || 0,
|
||||
submissionCount: 0,
|
||||
bestScore: null,
|
||||
passed: null,
|
||||
questions: (item.questions || []).map((question) => ({
|
||||
id: question.id,
|
||||
format: question.questionType,
|
||||
stem: question.stem,
|
||||
options: question.options || [],
|
||||
score: question.score,
|
||||
answer: question.correctAnswers || [],
|
||||
explanation: question.explanation || ''
|
||||
})),
|
||||
targets: item.targets || []
|
||||
});
|
||||
|
||||
const serializeDraft = (draft: SupervisorExamDraft) => ({
|
||||
title: draft.title,
|
||||
projectCode: draft.projectCode,
|
||||
positionCode: draft.positionCode,
|
||||
passScore: draft.passScore,
|
||||
dueDate: draft.dueDate,
|
||||
targets: draft.targets,
|
||||
questions: draft.questions.map((question, index) => ({
|
||||
id: question.id > 0 && question.id < 1_000_000_000 ? question.id : null,
|
||||
questionType: question.format,
|
||||
stem: question.stem,
|
||||
options: question.options,
|
||||
correctAnswers: Array.isArray(question.answer) ? question.answer : [String(question.answer || '')],
|
||||
score: question.score,
|
||||
explanation: question.explanation || '',
|
||||
sortOrder: index + 1
|
||||
}))
|
||||
});
|
||||
|
||||
export const listMyExams = async () =>
|
||||
(await apiRequest<RawExamSummary[]>({
|
||||
url: '/api/aihr/mobile/exams',
|
||||
method: 'GET',
|
||||
timeout: 15000
|
||||
})).map(normalizeSummary);
|
||||
|
||||
export const getMyExam = async (id: number) =>
|
||||
normalizeDetail(await apiRequest<RawExamDetail>({
|
||||
url: `/api/aihr/mobile/exams/${id}`,
|
||||
method: 'GET',
|
||||
timeout: 15000
|
||||
}));
|
||||
|
||||
export const submitMyExam = (id: number, answers: Record<string, string | string[]>, requestId: string) =>
|
||||
apiRequest<ExamSubmitResult>({
|
||||
url: `/api/aihr/mobile/exams/${id}/submit`,
|
||||
method: 'POST',
|
||||
data: {
|
||||
requestId,
|
||||
answers: Object.entries(answers).map(([questionId, value]) => ({
|
||||
questionId: Number(questionId),
|
||||
answers: Array.isArray(value) ? value : [String(value)]
|
||||
}))
|
||||
},
|
||||
timeout: 30000
|
||||
});
|
||||
|
||||
export const listSupervisorExams = async () =>
|
||||
(await apiRequest<RawExamSummary[]>({
|
||||
url: '/api/aihr/mobile/exams/supervisor',
|
||||
method: 'GET',
|
||||
timeout: 15000
|
||||
})).map(normalizeSummary);
|
||||
|
||||
export const getSupervisorExam = async (id: number) =>
|
||||
normalizeDetail(await apiRequest<RawExamDetail>({
|
||||
url: `/api/aihr/mobile/exams/supervisor/${id}`,
|
||||
method: 'GET',
|
||||
timeout: 15000
|
||||
}));
|
||||
|
||||
export const saveSupervisorExam = async (draft: SupervisorExamDraft) =>
|
||||
normalizeDetail(await apiRequest<RawExamDetail>({
|
||||
url: '/api/aihr/mobile/exams/supervisor',
|
||||
method: 'POST',
|
||||
data: serializeDraft(draft),
|
||||
timeout: 30000
|
||||
}));
|
||||
|
||||
export const updateSupervisorExam = async (id: number, draft: SupervisorExamDraft) =>
|
||||
normalizeDetail(await apiRequest<RawExamDetail>({
|
||||
url: `/api/aihr/mobile/exams/supervisor/${id}`,
|
||||
method: 'PUT',
|
||||
data: serializeDraft(draft),
|
||||
timeout: 30000
|
||||
}));
|
||||
|
||||
export const publishSupervisorExam = async (id: number, requestId: string) =>
|
||||
normalizeDetail(await apiRequest<RawExamDetail>({
|
||||
url: `/api/aihr/mobile/exams/supervisor/${id}/publish`,
|
||||
method: 'POST',
|
||||
data: { requestId },
|
||||
timeout: 30000
|
||||
}));
|
||||
|
||||
export const getSupervisorExamResults = (id: number) =>
|
||||
apiRequest<ExamResultItem[]>({
|
||||
url: `/api/aihr/mobile/exams/supervisor/${id}/results`,
|
||||
method: 'GET',
|
||||
timeout: 15000
|
||||
});
|
||||
Reference in New Issue
Block a user