@tinyweb_dev/oe-exam-sdk 0.1.31 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/exams/take/components/QuestionRenderer.js +6 -2
- package/dist/components/exams/take/components/question-renderers/MoversCrosswordPuzzleRenderer.d.ts +3 -0
- package/dist/components/exams/take/components/question-renderers/MoversCrosswordPuzzleRenderer.js +285 -0
- package/dist/components/exams/take/components/question-renderers/index.d.ts +1 -0
- package/dist/components/exams/take/components/question-renderers/index.js +1 -0
- package/dist/components/exams/take/types.d.ts +30 -0
- package/dist/components/exams/take/utils/answer-transformers.js +35 -5
- package/dist/components/exams/take/utils/question-transformers.d.ts +23 -1
- package/dist/components/exams/take/utils/question-transformers.js +36 -0
- package/dist/components/questions/_shared/config/question-types.config.js +6 -0
- package/dist/components/questions/_shared/types/crossword-puzzle.type.d.ts +16 -0
- package/dist/components/questions/_shared/types/crossword-puzzle.type.js +1 -0
- package/dist/components/questions/components/QuestionSearchDropdown.js +1 -1
- package/dist/components/questions/creator/question-type-registry.js +2 -0
- package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +2 -1
- package/dist/components/questions/question-bank/QuestionEditorRenderer.js +16 -0
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.d.ts +11 -0
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +80 -0
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.d.ts +8 -0
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +213 -0
- package/dist/components/questions/types/crossword-puzzle/index.d.ts +4 -0
- package/dist/components/questions/types/crossword-puzzle/index.js +4 -0
- package/dist/components/questions/types/crossword-puzzle/register.d.ts +2 -0
- package/dist/components/questions/types/crossword-puzzle/register.js +29 -0
- package/dist/components/questions/types/crossword-puzzle/transform.d.ts +2 -0
- package/dist/components/questions/types/crossword-puzzle/transform.js +75 -0
- package/dist/components/questions/viewer/QuestionViewer.js +7 -4
- package/dist/components/results/renderers/review-question-body-movers.js +7 -2
- package/dist/components/results/review-data.js +8 -4
- package/dist/shared/constants/question-skills.js +2 -0
- package/dist/shared/lib/utils/question-reverse-transform.js +54 -0
- package/dist/shared/lib/utils/question-transform-validation.js +12 -0
- package/dist/shared/lib/utils/question-transform.js +2 -0
- package/dist/shared/types/common.types.d.ts +1 -1
- package/dist/shared/types/questions/crossword-puzzle.d.ts +50 -0
- package/dist/shared/types/questions/crossword-puzzle.js +10 -0
- package/dist/shared/types/questions/index.d.ts +1 -0
- package/dist/shared/types/questions/index.js +2 -0
- package/package.json +1 -1
|
@@ -54,11 +54,23 @@ export function validateQuestionForTransform(question) {
|
|
|
54
54
|
case 'WRITE_A_SHORT_LETTER':
|
|
55
55
|
validateWordLimits(answer, errors);
|
|
56
56
|
break;
|
|
57
|
+
case 'CROSSWORD_PUZZLE':
|
|
58
|
+
validateCrossword(content, answer, errors);
|
|
59
|
+
break;
|
|
57
60
|
default:
|
|
58
61
|
break;
|
|
59
62
|
}
|
|
60
63
|
return { isValid: errors.length === 0, errors };
|
|
61
64
|
}
|
|
65
|
+
function validateCrossword(content, answer, errors) {
|
|
66
|
+
const clues = (content?.clues || answer?.clues);
|
|
67
|
+
const hasAcrossOrDown = (Array.isArray(clues?.across) && clues.across.length > 0) ||
|
|
68
|
+
(Array.isArray(clues?.down) && clues.down.length > 0) ||
|
|
69
|
+
(Array.isArray(clues) && clues.length > 0);
|
|
70
|
+
if (!hasAcrossOrDown) {
|
|
71
|
+
errors.push('Cần ít nhất 1 gợi ý (Across hoặc Down) cho bài ô chữ');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
62
74
|
function validateGrid(content, answer, errors) {
|
|
63
75
|
if (!Array.isArray(content?.grid))
|
|
64
76
|
errors.push('Grid is required for FILL_MISSING_WORDS_IN_GRID');
|
|
@@ -41,6 +41,7 @@ import { transformSpeakingConversation } from '../../../components/questions/typ
|
|
|
41
41
|
import { transformSpeakingCueCard } from '../../../components/questions/types/speaking-cue-card/transform';
|
|
42
42
|
import { transformGNSpeakingInterview } from '../../../components/questions/types/gn-speaking-interview/transform';
|
|
43
43
|
import { transformWordFillStructuredForm } from '../../../components/questions/types/word-fill-structured-form/transform';
|
|
44
|
+
import { transformCrosswordPuzzle } from '../../../components/questions/types/crossword-puzzle/transform';
|
|
44
45
|
// ─── Handler map ────────────────────────────────────────────────────────────
|
|
45
46
|
const questionTransformHandlers = {
|
|
46
47
|
// Look picture
|
|
@@ -88,6 +89,7 @@ const questionTransformHandlers = {
|
|
|
88
89
|
SPEAKING_CUE_CARD: transformSpeakingCueCard,
|
|
89
90
|
GN_SPEAKING_INTERVIEW: transformGNSpeakingInterview,
|
|
90
91
|
WORD_FILL_STRUCTURED_FORM: transformWordFillStructuredForm,
|
|
92
|
+
CROSSWORD_PUZZLE: transformCrosswordPuzzle,
|
|
91
93
|
};
|
|
92
94
|
/**
|
|
93
95
|
* Transform frontend question format to API format.
|
|
@@ -29,7 +29,7 @@ export declare enum StudentSelectionType {
|
|
|
29
29
|
}
|
|
30
30
|
export type ResultStatus = 'PENDING' | 'PARTIAL_GRADED' | 'GRADED';
|
|
31
31
|
export type StudentStatus = 'NOT_STARTED' | 'IN_PROGRESS' | 'SUBMITTED';
|
|
32
|
-
export type QuestionType = 'FILL_IN_BLANK' | 'TRUE_FALSE' | 'MATCHING' | 'ORDERING' | 'LISTENING' | 'COLORING' | 'ESSAY' | 'LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE' | 'LISTEN_AND_TICK_ANSWER' | 'LISTEN_AND_FILL_NAME_NUMBER' | 'LISTENING_FILL_BLANK' | 'LISTENING_COLOR' | 'TICK_TRUE_OR_FALSE' | 'TICK_YES_OR_NO' | 'READING_TICK_CROSS' | 'ARRANGE_LETTERS_INTO_WORDS' | 'READ_PASSAGE_AND_COMPLETE_STORY' | 'CLOZE_TEST_WITH_TICK_BOX' | 'READ_AND_CHOOSE_APPROPRIATE_WORD' | 'WRITE_SHORT_PARAGRAPH' | 'LABEL_THE_PICTURE' | 'READING_LETTER_ARRANGE' | 'READING_PASSAGE_FILL' | 'FILL_BLANK' | 'FILL_MISSING_WORDS_IN_GRID' | 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER' | 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER' | 'LOOK_PICTURE_FILL_WORD_HINT' | 'LABEL_THE_PICTURE' | 'READ_AND_COLOR_OBJECTS' | 'IMAGE_OBJECT_MATCHING' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'TRUE_FALSE_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'SPONTANEOUS_QA_GROUP' | 'WORD_ORDER_AND_MATCH_GROUP' | 'WRITE_A_SHORT_LETTER' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_CORRECT_ADJECTIVE' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'WORD_ORDERING' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'WRITE_SENTENCES' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPEAKING_CONVERSATION' | 'GN_SPEAKING_INTERVIEW' | 'SPEAKING_CUE_CARD' | 'WORD_FILL_STRUCTURED_FORM';
|
|
32
|
+
export type QuestionType = 'FILL_IN_BLANK' | 'TRUE_FALSE' | 'MATCHING' | 'ORDERING' | 'LISTENING' | 'COLORING' | 'ESSAY' | 'LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE' | 'LISTEN_AND_TICK_ANSWER' | 'LISTEN_AND_FILL_NAME_NUMBER' | 'LISTENING_FILL_BLANK' | 'LISTENING_COLOR' | 'TICK_TRUE_OR_FALSE' | 'TICK_YES_OR_NO' | 'READING_TICK_CROSS' | 'ARRANGE_LETTERS_INTO_WORDS' | 'READ_PASSAGE_AND_COMPLETE_STORY' | 'CLOZE_TEST_WITH_TICK_BOX' | 'READ_AND_CHOOSE_APPROPRIATE_WORD' | 'WRITE_SHORT_PARAGRAPH' | 'LABEL_THE_PICTURE' | 'READING_LETTER_ARRANGE' | 'READING_PASSAGE_FILL' | 'FILL_BLANK' | 'FILL_MISSING_WORDS_IN_GRID' | 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER' | 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER' | 'LOOK_PICTURE_FILL_WORD_HINT' | 'LABEL_THE_PICTURE' | 'READ_AND_COLOR_OBJECTS' | 'IMAGE_OBJECT_MATCHING' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'TRUE_FALSE_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'SPONTANEOUS_QA_GROUP' | 'WORD_ORDER_AND_MATCH_GROUP' | 'WRITE_A_SHORT_LETTER' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_CORRECT_ADJECTIVE' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'WORD_ORDERING' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'WRITE_SENTENCES' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPEAKING_CONVERSATION' | 'GN_SPEAKING_INTERVIEW' | 'SPEAKING_CUE_CARD' | 'WORD_FILL_STRUCTURED_FORM' | 'CROSSWORD_PUZZLE';
|
|
33
33
|
export type Level = 'Pre-A1' | 'A1' | 'A2' | 'B1' | 'B2' | 'C1' | 'C2';
|
|
34
34
|
export type Difficulty = 'EASY' | 'MEDIUM' | 'HARD';
|
|
35
35
|
export type Skill = 'LISTENING' | 'READING' | 'WRITING' | 'SPEAKING';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CROSSWORD_PUZZLE Question Type Interfaces
|
|
3
|
+
*
|
|
4
|
+
* This question type displays an interactive crossword grid with Across & Down clues.
|
|
5
|
+
*
|
|
6
|
+
* API format:
|
|
7
|
+
* - content: { title?, gridSize: { rows, cols }, clues: { across, down } }
|
|
8
|
+
* - correctAnswer: { answers: Record<string, string>, caseSensitive?: boolean }
|
|
9
|
+
*/
|
|
10
|
+
import { QuestionBankSettings } from './choose-the-correct-answer';
|
|
11
|
+
export interface CrosswordClue {
|
|
12
|
+
number: number;
|
|
13
|
+
clue: string;
|
|
14
|
+
startRow: number;
|
|
15
|
+
startCol: number;
|
|
16
|
+
length: number;
|
|
17
|
+
isExample?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export type CrosswordSubtype = 'STANDARD' | 'HIDDEN_WORD';
|
|
20
|
+
export interface CrosswordPuzzleContent {
|
|
21
|
+
type?: CrosswordSubtype;
|
|
22
|
+
title?: string;
|
|
23
|
+
gridSize: {
|
|
24
|
+
rows: number;
|
|
25
|
+
cols: number;
|
|
26
|
+
};
|
|
27
|
+
markedColumn?: number;
|
|
28
|
+
clues: {
|
|
29
|
+
across: CrosswordClue[];
|
|
30
|
+
down: CrosswordClue[];
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface CrosswordPuzzleCorrectAnswer {
|
|
34
|
+
answers: Record<string, string>;
|
|
35
|
+
caseSensitive?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface CrosswordPuzzleAPIPayload {
|
|
38
|
+
id?: string;
|
|
39
|
+
questionType: 'CROSSWORD_PUZZLE';
|
|
40
|
+
content: CrosswordPuzzleContent;
|
|
41
|
+
correctAnswer: CrosswordPuzzleCorrectAnswer;
|
|
42
|
+
points: number;
|
|
43
|
+
explanation?: string;
|
|
44
|
+
questionNumber: number;
|
|
45
|
+
partId?: string;
|
|
46
|
+
partNo?: number;
|
|
47
|
+
addToQuestionBank?: boolean;
|
|
48
|
+
questionBank?: QuestionBankSettings;
|
|
49
|
+
}
|
|
50
|
+
export type CrosswordPuzzleLocalStorage = Omit<CrosswordPuzzleAPIPayload, 'addToQuestionBank' | 'questionBank'>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CROSSWORD_PUZZLE Question Type Interfaces
|
|
3
|
+
*
|
|
4
|
+
* This question type displays an interactive crossword grid with Across & Down clues.
|
|
5
|
+
*
|
|
6
|
+
* API format:
|
|
7
|
+
* - content: { title?, gridSize: { rows, cols }, clues: { across, down } }
|
|
8
|
+
* - correctAnswer: { answers: Record<string, string>, caseSensitive?: boolean }
|
|
9
|
+
*/
|
|
10
|
+
export {};
|