@tinyweb_dev/oe-exam-sdk 0.2.13 → 1.0.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.
Files changed (99) hide show
  1. package/README.md +12 -0
  2. package/dist/components/exams/take/ExamTakingPageContainer.js +6 -0
  3. package/dist/components/exams/take/components/QuestionRenderer.js +13 -3
  4. package/dist/components/exams/take/components/question-renderers/ChooseThenAnswerGroupRenderer.d.ts +14 -0
  5. package/dist/components/exams/take/components/question-renderers/ChooseThenAnswerGroupRenderer.js +32 -0
  6. package/dist/components/exams/take/components/question-renderers/MoversAnswerTheQuestionRenderer.d.ts +1 -1
  7. package/dist/components/exams/take/components/question-renderers/MoversAnswerTheQuestionRenderer.js +10 -2
  8. package/dist/components/exams/take/components/question-renderers/SortWordsIntoCategoriesRenderer.d.ts +16 -0
  9. package/dist/components/exams/take/components/question-renderers/SortWordsIntoCategoriesRenderer.js +23 -0
  10. package/dist/components/exams/take/components/question-renderers/index.d.ts +2 -0
  11. package/dist/components/exams/take/components/question-renderers/index.js +2 -0
  12. package/dist/components/exams/take/utils/answer-transformers.js +34 -1
  13. package/dist/components/exams/take/utils/question-transformers.d.ts +42 -3
  14. package/dist/components/exams/take/utils/question-transformers.js +71 -1
  15. package/dist/components/questions/_shared/config/question-types.config.js +12 -0
  16. package/dist/components/questions/_shared/types/answer-the-question-group.type.d.ts +5 -1
  17. package/dist/components/questions/_shared/types/answer-the-question-group.type.js +7 -0
  18. package/dist/components/questions/_shared/types/answer-the-question.type.d.ts +3 -1
  19. package/dist/components/questions/_shared/types/answer-the-question.type.js +19 -1
  20. package/dist/components/questions/_shared/types/choose-then-answer-group.type.d.ts +47 -0
  21. package/dist/components/questions/_shared/types/choose-then-answer-group.type.js +2 -0
  22. package/dist/components/questions/_shared/types/index.d.ts +2 -0
  23. package/dist/components/questions/_shared/types/index.js +2 -0
  24. package/dist/components/questions/_shared/types/match-word-to-picture-group.type.d.ts +9 -3
  25. package/dist/components/questions/_shared/types/match-word-to-picture-group.type.js +19 -0
  26. package/dist/components/questions/_shared/types/sort-words-into-categories.type.d.ts +43 -0
  27. package/dist/components/questions/_shared/types/sort-words-into-categories.type.js +1 -0
  28. package/dist/components/questions/components/QuestionSearchDropdown.js +2 -0
  29. package/dist/components/questions/components/QuestionTypeSelector.js +2 -0
  30. package/dist/components/questions/creator/question-type-registry.js +4 -0
  31. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +30 -6
  32. package/dist/components/questions/types/answer-the-question/map-answer-the-question-data.d.ts +2 -0
  33. package/dist/components/questions/types/answer-the-question/map-answer-the-question-data.js +56 -0
  34. package/dist/components/questions/types/answer-the-question/transform.js +9 -3
  35. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +42 -9
  36. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +7 -5
  37. package/dist/components/questions/types/answer-the-question-group/map-answer-the-question-group-data.js +18 -4
  38. package/dist/components/questions/types/answer-the-question-group/transform.js +8 -2
  39. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -2
  40. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.d.ts +2 -0
  41. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +34 -0
  42. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupCreator.d.ts +2 -0
  43. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupCreator.js +174 -0
  44. package/dist/components/questions/types/choose-then-answer-group/index.d.ts +5 -0
  45. package/dist/components/questions/types/choose-then-answer-group/index.js +5 -0
  46. package/dist/components/questions/types/choose-then-answer-group/map-choose-then-answer-group-data.d.ts +11 -0
  47. package/dist/components/questions/types/choose-then-answer-group/map-choose-then-answer-group-data.js +127 -0
  48. package/dist/components/questions/types/choose-then-answer-group/register.d.ts +3 -0
  49. package/dist/components/questions/types/choose-then-answer-group/register.js +37 -0
  50. package/dist/components/questions/types/choose-then-answer-group/transform.d.ts +2 -0
  51. package/dist/components/questions/types/choose-then-answer-group/transform.js +37 -0
  52. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +39 -10
  53. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupCreator.js +15 -8
  54. package/dist/components/questions/types/match-word-to-picture-group/map-match-word-to-picture-group-data.js +8 -3
  55. package/dist/components/questions/types/match-word-to-picture-group/transform.js +21 -13
  56. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesBoard.d.ts +12 -0
  57. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesBoard.js +74 -0
  58. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.d.ts +2 -0
  59. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +24 -0
  60. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesCreator.d.ts +2 -0
  61. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesCreator.js +171 -0
  62. package/dist/components/questions/types/sort-words-into-categories/index.d.ts +4 -0
  63. package/dist/components/questions/types/sort-words-into-categories/index.js +4 -0
  64. package/dist/components/questions/types/sort-words-into-categories/map-sort-words-into-categories-data.d.ts +11 -0
  65. package/dist/components/questions/types/sort-words-into-categories/map-sort-words-into-categories-data.js +153 -0
  66. package/dist/components/questions/types/sort-words-into-categories/register.d.ts +3 -0
  67. package/dist/components/questions/types/sort-words-into-categories/register.js +37 -0
  68. package/dist/components/questions/types/sort-words-into-categories/transform.d.ts +2 -0
  69. package/dist/components/questions/types/sort-words-into-categories/transform.js +33 -0
  70. package/dist/components/questions/viewer/AnswerTheQuestionViewer.d.ts +9 -0
  71. package/dist/components/questions/viewer/AnswerTheQuestionViewer.js +8 -0
  72. package/dist/components/questions/viewer/ChooseThenAnswerGroupViewer.d.ts +10 -0
  73. package/dist/components/questions/viewer/ChooseThenAnswerGroupViewer.js +39 -0
  74. package/dist/components/questions/viewer/QuestionViewer.js +13 -2
  75. package/dist/components/questions/viewer/SortWordsIntoCategoriesViewer.d.ts +10 -0
  76. package/dist/components/questions/viewer/SortWordsIntoCategoriesViewer.js +27 -0
  77. package/dist/components/questions/viewer/index.d.ts +3 -0
  78. package/dist/components/questions/viewer/index.js +3 -0
  79. package/dist/components/results/renderers/ReviewAnswerTheQuestionGroupRenderer.d.ts +2 -1
  80. package/dist/components/results/renderers/ReviewAnswerTheQuestionGroupRenderer.js +12 -3
  81. package/dist/components/results/renderers/review-question-body-dedicated.js +1 -1
  82. package/dist/components/results/renderers/review-question-body-movers.js +24 -2
  83. package/dist/components/results/review-data.js +28 -0
  84. package/dist/index.d.ts +1 -1
  85. package/dist/index.js +1 -1
  86. package/dist/shared/constants/question-skills.js +4 -0
  87. package/dist/shared/lib/utils/question-reverse-transform.js +98 -2
  88. package/dist/shared/lib/utils/question-transform-types.d.ts +1 -1
  89. package/dist/shared/lib/utils/question-transform.js +4 -0
  90. package/dist/shared/types/common.types.d.ts +1 -1
  91. package/dist/shared/types/questions/answer-the-question-group.d.ts +3 -0
  92. package/dist/shared/types/questions/choose-then-answer-group.d.ts +46 -0
  93. package/dist/shared/types/questions/choose-then-answer-group.js +1 -0
  94. package/dist/shared/types/questions/index.d.ts +2 -0
  95. package/dist/shared/types/questions/index.js +3 -0
  96. package/dist/shared/types/questions/match-word-to-picture-group.d.ts +6 -2
  97. package/dist/shared/types/questions/sort-words-into-categories.d.ts +37 -0
  98. package/dist/shared/types/questions/sort-words-into-categories.js +1 -0
  99. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { fromFillInBlankImageUrls, toFillInBlankImageUrls, } from '../../../components/questions/types/fill-in-blank-group/blank-utils';
2
+ import { fromMatchWordToPictureImageUrls } from '../../../components/questions/_shared/types/match-word-to-picture-group.type';
2
3
  const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
3
4
  const asRecord = (value) => isRecord(value) ? value : {};
4
5
  const asArray = (value) => Array.isArray(value) ? value : [];
@@ -302,15 +303,18 @@ const reverseAnswerTheQuestionGroup = (apiQuestion) => {
302
303
  .filter((value) => value !== '');
303
304
  const expectedAnswers = nested.length > 0 ? nested : (fallbackExpected.length > 0 ? fallbackExpected : ['']);
304
305
  const isExample = item.isExample === true;
306
+ const imageUrl = normalizeChooseAnswerGroupImageUrl(itemContent.imageUrl);
305
307
  return {
306
308
  question: asString(itemContent.question),
307
309
  expectedAnswers,
308
310
  isExample,
309
311
  points: isExample ? 0 : asNumber(item.points, 1),
310
312
  questionNumber: asNumber(item.questionNumber, index + 1),
313
+ ...(imageUrl !== undefined ? { imageUrl } : {}),
311
314
  };
312
315
  });
313
316
  const meta = asRecord(content.meta);
317
+ const audioUrl = asString(meta.audioUrl).trim();
314
318
  const imageUrl = asArray(meta.imageUrl)
315
319
  .filter((value) => typeof value === 'string')
316
320
  .map((value) => value.trim())
@@ -321,6 +325,7 @@ const reverseAnswerTheQuestionGroup = (apiQuestion) => {
321
325
  answer: {
322
326
  instruction: asString(meta.instruction, 'Read the text again and answer the questions.'),
323
327
  passage: asString(meta.passage),
328
+ ...(audioUrl ? { audioUrl } : {}),
324
329
  ...(imageUrl.length ? { imageUrl } : (singleImage ? { imageUrl: [singleImage] } : {})),
325
330
  items,
326
331
  explanation: asString(apiQuestion.explanation),
@@ -391,6 +396,45 @@ const reverseTrueFalseCorrectGroup = (apiQuestion) => {
391
396
  },
392
397
  };
393
398
  };
399
+ const reverseChooseThenAnswerGroup = (apiQuestion) => {
400
+ const content = asRecord(apiQuestion.content);
401
+ const answer = readAnswer(apiQuestion);
402
+ const fallbackAnswers = asArray(answer.answer);
403
+ const meta = asRecord(content.meta);
404
+ const items = asArray(content.items).map((rawItem, index) => {
405
+ const item = asRecord(rawItem);
406
+ const itemContent = asRecord(item.content);
407
+ const itemAnswer = asRecord(asRecord(item.correctAnswer).answer);
408
+ const fallback = asRecord(fallbackAnswers[index]);
409
+ const rawAnswer = Object.keys(itemAnswer).length > 0 ? itemAnswer : fallback;
410
+ const isExample = item.isExample === true;
411
+ const options = asArray(itemContent.options).map((rawOption, optionIndex) => {
412
+ const option = asRecord(rawOption);
413
+ return {
414
+ id: asString(option.id, `opt-${optionIndex + 1}`),
415
+ text: asString(option.text),
416
+ };
417
+ });
418
+ return {
419
+ question: asString(itemContent.question),
420
+ options,
421
+ optionId: asString(rawAnswer.optionId) || options[0]?.id || '',
422
+ isExample,
423
+ points: isExample ? 0 : asNumber(item.points, 1),
424
+ questionNumber: asNumber(item.questionNumber, index + 1),
425
+ };
426
+ });
427
+ return {
428
+ content: '',
429
+ answer: {
430
+ title: asString(meta.title),
431
+ instruction: asString(meta.instruction, 'Read and choose. Then answer for you.'),
432
+ items,
433
+ explanation: asString(apiQuestion.explanation),
434
+ points: items.reduce((total, item) => (item.isExample ? total : total + item.points), 0),
435
+ },
436
+ };
437
+ };
394
438
  const reverseFillInBlankGroup = (apiQuestion) => {
395
439
  const content = asRecord(apiQuestion.content);
396
440
  const answer = readAnswer(apiQuestion);
@@ -680,8 +724,9 @@ const reverseMatchWordToPictureGroup = (apiQuestion) => {
680
724
  const itemContent = asRecord(item.content);
681
725
  const itemAnswer = asRecord(item.correctAnswer);
682
726
  const isExample = item.isExample === true;
727
+ const itemImageUrl = fromMatchWordToPictureImageUrls(itemContent.imageUrl ?? item.imageUrl);
683
728
  return {
684
- imageUrl: asString(itemContent.imageUrl),
729
+ ...(itemImageUrl ? { imageUrl: itemImageUrl } : {}),
685
730
  correctAnswer: asString(itemAnswer.answer) || asString(fallbackAnswers[index]),
686
731
  isExample,
687
732
  points: isExample ? 0 : (typeof item.points === 'number' ? item.points : 1),
@@ -693,7 +738,11 @@ const reverseMatchWordToPictureGroup = (apiQuestion) => {
693
738
  answer: {
694
739
  ...(asString(meta.title).trim() ? { title: asString(meta.title).trim() } : {}),
695
740
  instruction: asString(meta.instruction, 'Look and complete.'),
696
- ...(asString(meta.imageUrl).trim() ? { imageUrl: asString(meta.imageUrl).trim() } : {}),
741
+ ...(asString(meta.passage).trim() ? { passage: asString(meta.passage).trim() } : {}),
742
+ ...(asString(meta.audioUrl).trim() ? { audioUrl: asString(meta.audioUrl).trim() } : {}),
743
+ ...(fromMatchWordToPictureImageUrls(meta.imageUrl)
744
+ ? { imageUrl: fromMatchWordToPictureImageUrls(meta.imageUrl) }
745
+ : {}),
697
746
  wordBank: asArray(meta.wordBank).map((word) => asString(word)).filter(Boolean),
698
747
  items: mappedItems,
699
748
  explanation: asString(apiQuestion.explanation),
@@ -741,6 +790,7 @@ const handlers = {
741
790
  ANSWER_THE_QUESTION_GROUP: reverseAnswerTheQuestionGroup,
742
791
  TRUE_FALSE_GROUP: reverseTrueFalseGroup,
743
792
  TRUE_FALSE_CORRECT_GROUP: reverseTrueFalseCorrectGroup,
793
+ CHOOSE_THEN_ANSWER_GROUP: reverseChooseThenAnswerGroup,
744
794
  FILL_IN_BLANK_GROUP: reverseFillInBlankGroup,
745
795
  MATCHING_WITH_LINES_GROUP: reverseMatchingWithLinesGroup,
746
796
  CROSS_OUT_WORD_GROUP: reverseCrossOutWordGroup,
@@ -785,6 +835,7 @@ const handlers = {
785
835
  GN_SPEAKING_INTERVIEW: reverseGroupPayload,
786
836
  CROSSWORD_PUZZLE: reverseCrosswordPuzzle,
787
837
  FIND_WORDS_IN_MATRIX: reverseFindWordsInMatrix,
838
+ SORT_WORDS_INTO_CATEGORIES: reverseSortWordsIntoCategories,
788
839
  MATCH_COLUMNS_TO_MAKE_SENTENCES: reverseMatchColumnsToMakeSentences,
789
840
  MATCH_WORD_TO_PICTURE_GROUP: reverseMatchWordToPictureGroup,
790
841
  WORD_ORDERING_GROUP: reverseWordOrderingGroup,
@@ -959,6 +1010,51 @@ function reverseFindWordsInMatrix(apiQuestion) {
959
1010
  },
960
1011
  };
961
1012
  }
1013
+ function reverseSortWordsIntoCategories(apiQuestion) {
1014
+ const apiContent = asRecord(apiQuestion.content);
1015
+ const apiAnswer = readAnswer(apiQuestion);
1016
+ const meta = asRecord(apiContent.meta);
1017
+ const categories = asArray(apiContent.categories);
1018
+ const words = asArray(apiContent.words);
1019
+ const placements = asArray(apiAnswer.placements);
1020
+ const placementMap = new Map();
1021
+ placements.forEach((item) => {
1022
+ const record = asRecord(item);
1023
+ const wordId = asString(record.wordId);
1024
+ if (wordId && !placementMap.has(wordId)) {
1025
+ placementMap.set(wordId, asString(record.categoryId));
1026
+ }
1027
+ });
1028
+ const mappedWords = words.map((raw, index) => {
1029
+ const item = asRecord(raw);
1030
+ const id = asString(item.id, `w${index + 1}`);
1031
+ const isExample = item.isExample === true;
1032
+ return {
1033
+ id,
1034
+ text: asString(item.text),
1035
+ categoryId: placementMap.get(id) || '',
1036
+ isExample,
1037
+ points: isExample ? 0 : (typeof item.points === 'number' ? item.points : 1),
1038
+ };
1039
+ });
1040
+ return {
1041
+ content: '',
1042
+ answer: {
1043
+ title: asString(meta.title),
1044
+ instruction: asString(meta.instruction, 'Read and sort the words.'),
1045
+ categories: categories.map((raw, index) => {
1046
+ const item = asRecord(raw);
1047
+ return {
1048
+ id: asString(item.id, `cat_${index + 1}`),
1049
+ name: asString(item.name),
1050
+ };
1051
+ }),
1052
+ words: mappedWords,
1053
+ explanation: asString(apiQuestion.explanation),
1054
+ points: mappedWords.reduce((total, word) => (word.isExample ? total : total + (word.points || 0)), 0),
1055
+ },
1056
+ };
1057
+ }
962
1058
  export function transformApiQuestionToFrontend(apiQuestion) {
963
1059
  const questionType = String(apiQuestion.questionType ?? apiQuestion.question_type ?? apiQuestion.type ?? '');
964
1060
  const handlerResult = (handlers[questionType] ?? defaultReverse)(apiQuestion);
@@ -312,7 +312,7 @@ export interface AnswerTheQuestionAnswerData {
312
312
  groupNumber?: number;
313
313
  isExample?: boolean;
314
314
  suggestedAnswers?: string[];
315
- imageUrl?: string;
315
+ imageUrl?: string | string[];
316
316
  title?: string;
317
317
  subtitle?: string;
318
318
  groupContent?: string;
@@ -47,6 +47,8 @@ import { transformGNSpeakingInterview } from '../../../components/questions/type
47
47
  import { transformWordFillStructuredForm } from '../../../components/questions/types/word-fill-structured-form/transform';
48
48
  import { transformCrosswordPuzzle } from '../../../components/questions/types/crossword-puzzle/transform';
49
49
  import { transformFindWordsInMatrix } from '../../../components/questions/types/find-words-in-matrix/transform';
50
+ import { transformSortWordsIntoCategories } from '../../../components/questions/types/sort-words-into-categories/transform';
51
+ import { transformChooseThenAnswerGroup } from '../../../components/questions/types/choose-then-answer-group/transform';
50
52
  import { transformMatchColumnsToMakeSentences } from '../../../components/questions/types/match-columns-to-make-sentences/transform';
51
53
  import { transformMatchWordToPictureGroup } from '../../../components/questions/types/match-word-to-picture-group/transform';
52
54
  import { transformWordOrderingGroup } from '../../../components/questions/types/word-ordering-group/transform';
@@ -103,6 +105,8 @@ const questionTransformHandlers = {
103
105
  WORD_FILL_STRUCTURED_FORM: transformWordFillStructuredForm,
104
106
  CROSSWORD_PUZZLE: transformCrosswordPuzzle,
105
107
  FIND_WORDS_IN_MATRIX: transformFindWordsInMatrix,
108
+ SORT_WORDS_INTO_CATEGORIES: transformSortWordsIntoCategories,
109
+ CHOOSE_THEN_ANSWER_GROUP: transformChooseThenAnswerGroup,
106
110
  MATCH_COLUMNS_TO_MAKE_SENTENCES: transformMatchColumnsToMakeSentences,
107
111
  MATCH_WORD_TO_PICTURE_GROUP: transformMatchWordToPictureGroup,
108
112
  WORD_ORDERING_GROUP: transformWordOrderingGroup,
@@ -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' | 'TRUE_FALSE_CORRECT_GROUP' | 'FILL_IN_BLANK_GROUP' | 'MATCHING_WITH_LINES_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' | 'MATCH_WORD_TO_PICTURE' | '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' | 'FIND_WORDS_IN_MATRIX' | 'MATCH_COLUMNS_TO_MAKE_SENTENCES' | 'MATCH_WORD_TO_PICTURE_GROUP' | 'WORD_ORDERING_GROUP';
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' | 'TRUE_FALSE_CORRECT_GROUP' | 'FILL_IN_BLANK_GROUP' | 'MATCHING_WITH_LINES_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' | 'MATCH_WORD_TO_PICTURE' | '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' | 'FIND_WORDS_IN_MATRIX' | 'SORT_WORDS_INTO_CATEGORIES' | 'CHOOSE_THEN_ANSWER_GROUP' | 'MATCH_COLUMNS_TO_MAKE_SENTENCES' | 'MATCH_WORD_TO_PICTURE_GROUP' | 'WORD_ORDERING_GROUP';
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';
@@ -1,6 +1,7 @@
1
1
  export declare const ANSWER_THE_QUESTION_GROUP_GRADING_TYPE: "AI";
2
2
  export interface AnswerTheQuestionGroupItemContent {
3
3
  question: string;
4
+ imageUrl?: string | string[];
4
5
  }
5
6
  export interface AnswerTheQuestionGroupItemExpectedAnswer {
6
7
  expectedAnswers: string[];
@@ -17,6 +18,8 @@ export interface AnswerTheQuestionGroupContent {
17
18
  meta?: {
18
19
  instruction?: string;
19
20
  passage?: string;
21
+ audioUrl?: string;
22
+ imageUrl?: string | string[];
20
23
  gradingType: typeof ANSWER_THE_QUESTION_GROUP_GRADING_TYPE;
21
24
  isAiGraded: true;
22
25
  };
@@ -0,0 +1,46 @@
1
+ export interface ChooseThenAnswerOption {
2
+ id: string;
3
+ text: string;
4
+ }
5
+ export interface ChooseThenAnswerItemAnswer {
6
+ optionId: string;
7
+ }
8
+ export interface ChooseThenAnswerStudentItemAnswer {
9
+ optionId: string;
10
+ text: string;
11
+ }
12
+ export interface ChooseThenAnswerGroupItemContent {
13
+ question: string;
14
+ options: ChooseThenAnswerOption[];
15
+ }
16
+ export interface ChooseThenAnswerGroupItem {
17
+ questionType?: string;
18
+ isExample?: boolean;
19
+ content: ChooseThenAnswerGroupItemContent;
20
+ correctAnswer?: {
21
+ answer: ChooseThenAnswerItemAnswer;
22
+ };
23
+ points?: number;
24
+ questionNumber?: number;
25
+ }
26
+ export interface ChooseThenAnswerGroupContent {
27
+ meta?: {
28
+ instruction?: string;
29
+ title?: string;
30
+ };
31
+ items: ChooseThenAnswerGroupItem[];
32
+ }
33
+ export interface ChooseThenAnswerGroupCorrectAnswer {
34
+ answer: ChooseThenAnswerItemAnswer[];
35
+ }
36
+ export interface ChooseThenAnswerGroupAPIPayload {
37
+ id?: string;
38
+ questionType: 'CHOOSE_THEN_ANSWER_GROUP';
39
+ content: ChooseThenAnswerGroupContent;
40
+ correctAnswer: ChooseThenAnswerGroupCorrectAnswer;
41
+ points: number;
42
+ explanation?: string;
43
+ questionNumber: number;
44
+ partId?: string;
45
+ partNo?: number;
46
+ }
@@ -32,6 +32,8 @@ export * from './spontaneous-qa';
32
32
  export * from './word-order-and-match-group';
33
33
  export * from './crossword-puzzle';
34
34
  export * from './find-words-in-matrix';
35
+ export * from './sort-words-into-categories';
36
+ export * from './choose-then-answer-group';
35
37
  export * from './match-word-to-picture';
36
38
  export * from './matching-with-lines-group';
37
39
  export * from './match-columns-to-make-sentences';
@@ -50,6 +50,9 @@ export * from './word-order-and-match-group';
50
50
  export * from './crossword-puzzle';
51
51
  // FIND_WORDS_IN_MATRIX
52
52
  export * from './find-words-in-matrix';
53
+ // SORT_WORDS_INTO_CATEGORIES
54
+ export * from './sort-words-into-categories';
55
+ export * from './choose-then-answer-group';
53
56
  // MATCH_WORD_TO_PICTURE
54
57
  export * from './match-word-to-picture';
55
58
  // MATCHING_WITH_LINES_GROUP
@@ -3,7 +3,7 @@ export interface MatchWordToPictureGroupItem {
3
3
  questionType?: typeof MATCH_WORD_TO_PICTURE_ITEM_TYPE;
4
4
  isExample?: boolean;
5
5
  content: {
6
- imageUrl?: string;
6
+ imageUrl?: string | string[];
7
7
  };
8
8
  correctAnswer?: {
9
9
  answer: string;
@@ -15,7 +15,11 @@ export interface MatchWordToPictureGroupContent {
15
15
  meta?: {
16
16
  title?: string;
17
17
  instruction?: string;
18
- imageUrl?: string;
18
+ /** Shared reading passage (HTML) above the match-word-to-picture UI. */
19
+ passage?: string;
20
+ /** Shared listening audio played once under the instruction. */
21
+ audioUrl?: string;
22
+ imageUrl?: string | string[];
19
23
  wordBank?: string[];
20
24
  };
21
25
  items: MatchWordToPictureGroupItem[];
@@ -0,0 +1,37 @@
1
+ export interface SortWordsCategory {
2
+ id: string;
3
+ name: string;
4
+ }
5
+ export interface SortWordsWord {
6
+ id: string;
7
+ text: string;
8
+ points?: number;
9
+ isExample?: boolean;
10
+ }
11
+ export interface SortWordsPlacement {
12
+ wordId: string;
13
+ categoryId: string;
14
+ }
15
+ export interface SortWordsIntoCategoriesContent {
16
+ meta?: {
17
+ instruction?: string;
18
+ title?: string;
19
+ };
20
+ categories: SortWordsCategory[];
21
+ words: SortWordsWord[];
22
+ }
23
+ export interface SortWordsIntoCategoriesCorrectAnswer {
24
+ placements: SortWordsPlacement[];
25
+ }
26
+ export interface SortWordsIntoCategoriesAPIPayload {
27
+ id?: string;
28
+ questionType: 'SORT_WORDS_INTO_CATEGORIES';
29
+ content: SortWordsIntoCategoriesContent;
30
+ correctAnswer: SortWordsIntoCategoriesCorrectAnswer;
31
+ points: number;
32
+ explanation?: string;
33
+ questionNumber: number;
34
+ partId?: string;
35
+ partNo?: number;
36
+ }
37
+ export type SortWordsIntoCategoriesLocalStorage = SortWordsIntoCategoriesAPIPayload;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinyweb_dev/oe-exam-sdk",
3
- "version": "0.2.13",
3
+ "version": "1.0.0",
4
4
  "description": "Reusable OceanEdu question components.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",