@tinyweb_dev/oe-exam-sdk 1.0.6 → 1.0.8
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/ExamCreator.js +1 -16
- package/dist/components/exams/take/ExamTakingPageContainer.js +0 -12
- package/dist/components/exams/take/components/QuestionRenderer.js +2 -15
- package/dist/components/exams/take/components/question-renderers/index.d.ts +0 -3
- package/dist/components/exams/take/components/question-renderers/index.js +0 -3
- package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +0 -4
- package/dist/components/exams/take/types/speaking.types.d.ts +0 -31
- package/dist/components/exams/take/types.d.ts +0 -65
- package/dist/components/exams/take/utils/answer-transformers.js +6 -60
- package/dist/components/exams/take/utils/question-transformers.d.ts +2 -15
- package/dist/components/exams/take/utils/question-transformers.js +0 -143
- package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.d.ts +23 -0
- package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.js +9 -0
- package/dist/components/questions/_shared/components/compact/CompactExampleToggle.d.ts +6 -0
- package/dist/components/questions/_shared/components/compact/CompactExampleToggle.js +8 -0
- package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.d.ts +7 -0
- package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.js +14 -0
- package/dist/components/questions/_shared/components/compact/CompactFieldCard.d.ts +9 -0
- package/dist/components/questions/_shared/components/compact/CompactFieldCard.js +7 -0
- package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.d.ts +7 -0
- package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.js +7 -0
- package/dist/components/questions/_shared/components/compact/CompactPreviewButton.d.ts +6 -0
- package/dist/components/questions/_shared/components/compact/CompactPreviewButton.js +7 -0
- package/dist/components/questions/_shared/components/compact/index.d.ts +6 -0
- package/dist/components/questions/_shared/components/compact/index.js +6 -0
- package/dist/components/questions/_shared/config/question-types.config.js +0 -24
- package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +0 -9
- package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.d.ts +2 -2
- package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.js +5 -1
- package/dist/components/questions/_shared/types/index.d.ts +0 -1
- package/dist/components/questions/_shared/types/index.js +0 -1
- package/dist/components/questions/_shared/types/look-and-choose-answer.type.d.ts +0 -13
- package/dist/components/questions/_shared/utils/answer-builder.js +0 -11
- package/dist/components/questions/components/ColorRegionCreator.js +66 -63
- package/dist/components/questions/components/QuestionSearchDropdown.js +0 -8
- package/dist/components/questions/components/QuestionTypeSelector.js +0 -2
- package/dist/components/questions/components/index.d.ts +1 -0
- package/dist/components/questions/components/index.js +1 -0
- package/dist/components/questions/creator/question-type-registry.js +0 -8
- package/dist/components/questions/groups/ArticlesGroupCard.js +2 -2
- package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -1
- package/dist/components/questions/groups/DocumentGroupCard.js +1 -1
- package/dist/components/questions/groups/ImageLabelGroupCard.js +1 -1
- package/dist/components/questions/groups/MatchAnswerGroupCard.js +2 -2
- package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +0 -3
- package/dist/components/questions/question-bank/QuestionEditorRenderer.js +1 -10
- package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +0 -2
- package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +0 -41
- package/dist/components/questions/question-bank/editor-adapters/index.d.ts +2 -2
- package/dist/components/questions/question-bank/editor-adapters/index.js +2 -2
- package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +0 -1
- package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +0 -28
- package/dist/components/questions/question-bank/question-bank-picker.utils.js +0 -7
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +16 -16
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +36 -30
- package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +1 -1
- package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +7 -6
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +2 -2
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +11 -7
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -1
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +11 -5
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +5 -5
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +34 -28
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.d.ts +7 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.js +5 -2
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +9 -5
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +65 -62
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupItemEditor.d.ts +6 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupItemEditor.js +145 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.js +6 -15
- package/dist/components/questions/types/choose-the-correct-answer-group/transform.js +5 -4
- package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +7 -7
- package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupCreator.js +8 -5
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +2 -2
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +20 -16
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +1 -1
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +66 -29
- package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +5 -5
- package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +45 -37
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +24 -20
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +2 -2
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +68 -65
- package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +1 -1
- package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +45 -5
- package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +2 -2
- package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +18 -14
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +2 -2
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +30 -30
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +2 -2
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +24 -31
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +2 -2
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +29 -32
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +27 -29
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +11 -15
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +22 -24
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +17 -24
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +31 -31
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +11 -15
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +14 -15
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +2 -2
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +27 -25
- package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesClient.js +12 -12
- package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesCreator.js +8 -5
- package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureClient.js +8 -8
- package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureCreator.js +25 -20
- package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +1 -1
- package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupCreator.js +13 -10
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js +8 -5
- package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +7 -11
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -1
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +15 -12
- package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +1 -1
- package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesCreator.js +5 -5
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +35 -39
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +2 -2
- package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardClient.js +1 -1
- package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +7 -8
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -1
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +30 -23
- package/dist/components/questions/types/spontaneous-qa/SpontaneousQAClient.js +1 -1
- package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +26 -19
- package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.d.ts +1 -1
- package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +79 -3
- package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +13 -11
- package/dist/components/questions/types/spontaneous-qa-group/index.d.ts +1 -0
- package/dist/components/questions/types/spontaneous-qa-group/index.js +1 -0
- package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.d.ts +2 -0
- package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.js +74 -0
- package/dist/components/questions/types/true-false/TrueFalseClient.js +2 -2
- package/dist/components/questions/types/true-false/TrueFalseCreator.js +35 -29
- package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.js +1 -1
- package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupCreator.js +11 -7
- package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +1 -1
- package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +11 -7
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +2 -2
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +21 -56
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +30 -24
- package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +3 -3
- package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +12 -14
- package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -1
- package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +27 -18
- package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +7 -7
- package/dist/components/questions/types/word-ordering-group/WordOrderingGroupCreator.js +16 -13
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +2 -2
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +34 -28
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +2 -2
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +22 -22
- package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +21 -18
- package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -1
- package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +7 -4
- package/dist/components/questions/viewer/QuestionViewer.js +0 -98
- package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +4 -79
- package/dist/components/results/renderers/review-question-body-movers.js +2 -26
- package/dist/components/results/review-data.js +0 -4
- package/dist/components/ui/points-input.d.ts +1 -0
- package/dist/components/ui/points-input.js +12 -2
- package/dist/shared/constants/question-skills.js +0 -11
- package/dist/shared/lib/i18n/messages/en/admin.d.ts +0 -3
- package/dist/shared/lib/i18n/messages/en/admin.js +0 -3
- package/dist/shared/lib/i18n/messages/en.d.ts +0 -3
- package/dist/shared/lib/i18n/messages/vi/admin.d.ts +0 -3
- package/dist/shared/lib/i18n/messages/vi/admin.js +0 -3
- package/dist/shared/lib/i18n/messages/vi.d.ts +0 -3
- package/dist/shared/lib/utils/question-reverse-transform.js +4 -23
- package/dist/shared/lib/utils/question-transform-types.d.ts +1 -23
- package/dist/shared/lib/utils/question-transform-validation.js +0 -40
- package/dist/shared/lib/utils/question-transform.js +0 -9
- package/dist/shared/types/common.types.d.ts +1 -1
- package/dist/shared/types/entities/question.types.d.ts +1 -11
- package/dist/shared/types/questions/choose-the-correct-answer-group.d.ts +1 -1
- package/dist/shared/types/questions/index.d.ts +0 -3
- package/dist/shared/types/questions/index.js +0 -6
- package/package.json +1 -1
- package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.d.ts +0 -2
- package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +0 -131
- package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.d.ts +0 -2
- package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +0 -76
- package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.d.ts +0 -2
- package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +0 -68
- package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.d.ts +0 -16
- package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +0 -212
- package/dist/components/questions/_shared/types/gn-speaking-interview.type.d.ts +0 -32
- package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +0 -1
- package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.d.ts +0 -47
- package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +0 -1
- package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.d.ts +0 -2
- package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +0 -262
- package/dist/components/questions/types/gn-speaking-interview/register.d.ts +0 -2
- package/dist/components/questions/types/gn-speaking-interview/register.js +0 -121
- package/dist/components/questions/types/gn-speaking-interview/transform.d.ts +0 -2
- package/dist/components/questions/types/gn-speaking-interview/transform.js +0 -48
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.d.ts +0 -20
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +0 -72
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.d.ts +0 -15
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +0 -230
- package/dist/components/questions/types/look-picture-choose-correct-answer/index.d.ts +0 -1
- package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +0 -1
- package/dist/components/questions/types/look-picture-choose-correct-answer/register.d.ts +0 -2
- package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +0 -66
- package/dist/components/questions/types/look-picture-choose-correct-answer/transform.d.ts +0 -2
- package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +0 -42
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.d.ts +0 -6
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +0 -96
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.d.ts +0 -15
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +0 -238
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +0 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +0 -76
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +0 -19
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.d.ts +0 -7
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +0 -134
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.d.ts +0 -3
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +0 -617
- package/dist/components/questions/types/look-picture-fill-word-hint/index.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/index.js +0 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/register.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/register.js +0 -82
- package/dist/components/questions/types/look-picture-fill-word-hint/transform.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +0 -29
- package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.d.ts +0 -9
- package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +0 -12
- package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.d.ts +0 -17
- package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +0 -47
- package/dist/shared/types/questions/look-picture-choose-correct-answer.d.ts +0 -38
- package/dist/shared/types/questions/look-picture-choose-correct-answer.js +0 -5
- package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.d.ts +0 -63
- package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +0 -9
- package/dist/shared/types/questions/look-picture-fill-word-hint.d.ts +0 -57
- package/dist/shared/types/questions/look-picture-fill-word-hint.js +0 -7
|
@@ -38,7 +38,6 @@ export function ExamCreator(props) {
|
|
|
38
38
|
const clearQuestionData = useExamQuestionStore((state) => state.clearQuestionData);
|
|
39
39
|
const markQuestionCompleted = useExamQuestionStore((state) => state.markQuestionCompleted);
|
|
40
40
|
const getQuestionData = useExamQuestionStore((state) => state.getQuestionData);
|
|
41
|
-
const setSharedHintLetters = useExamQuestionStore((state) => state.setSharedHintLetters);
|
|
42
41
|
const [validationErrors, setValidationErrors] = useState({});
|
|
43
42
|
const [isSavingQuestion, setIsSavingQuestion] = useState(false);
|
|
44
43
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
@@ -96,14 +95,13 @@ export function ExamCreator(props) {
|
|
|
96
95
|
upsertQuestionData(index, savedQuestion);
|
|
97
96
|
markQuestionCompleted(index, true);
|
|
98
97
|
savedQuestionsRef.current[index] = savedQuestion;
|
|
99
|
-
hydrateSharedHintLetters(index, savedQuestion, template, setSharedHintLetters);
|
|
100
98
|
const questionBank = question.questionBank;
|
|
101
99
|
if (questionBank?.id)
|
|
102
100
|
setExistingQuestionBanks((prev) => ({ ...prev, [index]: questionBank }));
|
|
103
101
|
});
|
|
104
102
|
const timer = window.setTimeout(() => { justInitializedRef.current = false; }, 1000);
|
|
105
103
|
return () => window.clearTimeout(timer);
|
|
106
|
-
}, [initialQuestions, initialized, markQuestionCompleted,
|
|
104
|
+
}, [initialQuestions, initialized, markQuestionCompleted, template, upsertQuestionData]);
|
|
107
105
|
useEffect(() => {
|
|
108
106
|
setCurrentQuestionHasUnsavedChanges(getQuestionStatus(examId, currentQuestionIndex + 1) === QuestionStatus.UNSAVED);
|
|
109
107
|
}, [currentQuestionIndex, examId]);
|
|
@@ -302,19 +300,6 @@ function validateQuestionBank(setting, existingBank, setCodeError, setTitleError
|
|
|
302
300
|
setTitleError(titleError);
|
|
303
301
|
return !codeError && !titleError;
|
|
304
302
|
}
|
|
305
|
-
function hydrateSharedHintLetters(index, question, template, setSharedHintLetters) {
|
|
306
|
-
if (question.type !== 'LOOK_PICTURE_FILL_WORD_HINT' || !question.partId)
|
|
307
|
-
return;
|
|
308
|
-
const answer = question.answer;
|
|
309
|
-
const groupNumber = question.questionGroup?.groupNumber;
|
|
310
|
-
if (!answer?.hintLetters?.length || !groupNumber)
|
|
311
|
-
return;
|
|
312
|
-
const part = getPartByQuestionIndex(template, index);
|
|
313
|
-
const config = part ? getEffectiveQuestionConfig(part, index) : undefined;
|
|
314
|
-
const sectionName = getConfigSectionName(config);
|
|
315
|
-
const partId = sectionName ? `${question.partId}_${sectionName}` : question.partId;
|
|
316
|
-
setSharedHintLetters(partId, groupNumber, answer.hintLetters);
|
|
317
|
-
}
|
|
318
303
|
function getConfigSectionName(config) {
|
|
319
304
|
return config && 'sectionName' in config ? config.sectionName : undefined;
|
|
320
305
|
}
|
|
@@ -614,18 +614,6 @@ function ExamTakingPageContainerInner({ roomId, api, onNavigate, resultPath = (a
|
|
|
614
614
|
map.set(region.id, question.id);
|
|
615
615
|
});
|
|
616
616
|
}
|
|
617
|
-
// LOOK_PICTURE_FILL_WORD_HINT: single question with {0}, {1} placeholders → synthetic IDs
|
|
618
|
-
if (part.questionType === 'LOOK_PICTURE_FILL_WORD_HINT') {
|
|
619
|
-
const word = content?.word;
|
|
620
|
-
if (word && typeof word === 'string') {
|
|
621
|
-
const placeholderRegex = /\{(\d+)\}/g;
|
|
622
|
-
let match;
|
|
623
|
-
while ((match = placeholderRegex.exec(word)) !== null) {
|
|
624
|
-
const blankIndex = match[1];
|
|
625
|
-
map.set(`${question.id}-blank-${blankIndex}`, question.id);
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
617
|
if (part.questionType === 'CROSS_OUT_WORD_GROUP' &&
|
|
630
618
|
Array.isArray(content?.items)) {
|
|
631
619
|
content.items.forEach((_, index) => {
|
|
@@ -8,9 +8,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
* 2. Transforms data to renderer-specific props
|
|
9
9
|
* 3. Renders the correct component for each question type
|
|
10
10
|
*/
|
|
11
|
-
import { MoversListenAndWriteRenderer, MoversChooseBestAnswerRenderer, MoversChooseAdjectiveRenderer, MoversWritingRenderer, MoversColoringRenderer,
|
|
11
|
+
import { MoversListenAndWriteRenderer, MoversChooseBestAnswerRenderer, MoversChooseAdjectiveRenderer, MoversWritingRenderer, MoversColoringRenderer, MoversReadingPassageRenderer, MoversLabelThePictureRenderer, MoversGridFillRenderer, MoversTrueFalseRenderer, MoversTrueFalseCorrectGroupRenderer, MoversFillInBlankGroupRenderer, MoversCrossOutWordGroupRenderer, MoversAnswerTheQuestionRenderer, MoversWriteSentencesRenderer, MoversWordOrderingRenderer, MoversWordOrderingGroupRenderer, MoversWordFillParagraphRenderer, MoversMatchByWritingAnswerRenderer, MoversMatchWordToPictureRenderer, MoversWordFillStructuredFormRenderer, MoversWordOrderAndMatchGroupRenderer, MoversMatchingWithLinesGroupRenderer, MoversCrosswordPuzzleRenderer, FindWordsInMatrixRenderer, SortWordsIntoCategoriesRenderer, ChooseThenAnswerGroupRenderer, } from './question-renderers';
|
|
12
12
|
import { SpeakingQuestionRenderer } from './speaking/renderers';
|
|
13
|
-
import { transformFillInBlank, transformWriteCorrectVerbForm, transformChooseCorrectAnswer, transformChooseTheCorrectAnswerGroup, transformChooseCorrectAdjective, transformWriteShortLetter, transformColorObjects,
|
|
13
|
+
import { transformFillInBlank, transformWriteCorrectVerbForm, transformChooseCorrectAnswer, transformChooseTheCorrectAnswerGroup, transformChooseCorrectAdjective, transformWriteShortLetter, transformColorObjects, transformReadingPassage, transformLabelPicture, transformGridFill, transformAnswerTheQuestion, transformAnswerTheQuestionGroup, transformTrueFalse, transformTrueFalseGroup, transformTrueFalseCorrectGroup, transformFillInBlankGroup, transformWordOrdering, transformWordOrderingGroupTake, transformWordFillParagraph, transformMatchByWritingAnswer, transformMatchWordToPicture, transformWriteSentences, transformWordFillStructuredForm, transformWordOrderAndMatchGroup, transformMatchingWithLinesGroup, transformCrosswordPuzzle, transformFindWordsInMatrix, transformSortWordsIntoCategories, transformChooseThenAnswerGroup, } from '../utils/question-transformers';
|
|
14
14
|
import { transformCrossOutWordGroup } from '../utils/cross-out-word-group.transformer';
|
|
15
15
|
import { normalizeColoringAssignments } from '../utils/coloring-assignments';
|
|
16
16
|
export function QuestionRenderer({ part, answers, onAnswerChange, isReviewMode = false, onExit, onPartComplete, onPartBack, speakingTheme, initialQuestionIndex, skipTeacherVideo, onTeacherIntroPlayed, currentQuestionIndex, compactLayout = false, }) {
|
|
@@ -88,14 +88,6 @@ export function QuestionRenderer({ part, answers, onAnswerChange, isReviewMode =
|
|
|
88
88
|
onAnswerChange(regionId, hintItemId);
|
|
89
89
|
}, isReviewMode: isReviewMode }));
|
|
90
90
|
}
|
|
91
|
-
case 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER': {
|
|
92
|
-
const data = transformPictureChoose(questions);
|
|
93
|
-
return (_jsx(MoversPictureChooseRenderer, { partNumber: partNo, partName: part.name, questionCount: questionCount, instruction: part.instructions || data.instruction, subQuestions: data.subQuestions, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
94
|
-
}
|
|
95
|
-
case 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER': {
|
|
96
|
-
const data = transformPictureFillBlankChoose(questions);
|
|
97
|
-
return (_jsx(MoversPictureFillBlankChooseRenderer, { partNumber: partNo, partName: part.name, questionCount: questionCount, instruction: part.instructions || data.instruction, subQuestions: data.subQuestions, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
98
|
-
}
|
|
99
91
|
case 'READ_PASSAGE_AND_ANSWER_QUESTIONS': {
|
|
100
92
|
const data = transformReadingPassage(questions);
|
|
101
93
|
return (_jsx(MoversReadingPassageRenderer, { partNumber: partNo, partName: part.name, questionCount: data.questions.length, instruction: part.instructions || data.instruction, passageTitle: data.passageTitle, passage: data.passage, passageImageUrl: data.passageImageUrl, questions: getRenderQuestions(data.questions), answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
@@ -104,10 +96,6 @@ export function QuestionRenderer({ part, answers, onAnswerChange, isReviewMode =
|
|
|
104
96
|
const data = transformLabelPicture(questions);
|
|
105
97
|
return (_jsx(MoversLabelThePictureRenderer, { partNumber: partNo, partName: part.name, questionCount: data.labels.length, instruction: part.instructions || data.instruction, imageUrl: data.imageUrl, wordBank: data.wordBank, labels: data.labels, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
106
98
|
}
|
|
107
|
-
case 'LOOK_PICTURE_FILL_WORD_HINT': {
|
|
108
|
-
const data = transformFillWordHint(questions);
|
|
109
|
-
return (_jsx(MoversFillWordHintRenderer, { partNumber: partNo, partName: part.name, questionCount: data.subQuestions.length, instruction: part.instructions || data.instruction, hintLetters: data.hintLetters, subQuestions: data.subQuestions, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
110
|
-
}
|
|
111
99
|
case 'FILL_MISSING_WORDS_IN_GRID': {
|
|
112
100
|
const data = transformGridFill(questions);
|
|
113
101
|
return (_jsx(MoversGridFillRenderer, { partNumber: partNo, partName: part.name, questionCount: questionCount, instruction: part.instructions || data.instruction, grid: data.grid, rows: data.rows, columns: data.columns, cellAnswers: {}, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode, disabledCells: data.disabledCells }));
|
|
@@ -243,7 +231,6 @@ export function QuestionRenderer({ part, answers, onAnswerChange, isReviewMode =
|
|
|
243
231
|
case 'SPONTANEOUS_QA':
|
|
244
232
|
case 'SPONTANEOUS_QA_GROUP':
|
|
245
233
|
case 'SPEAKING_CONVERSATION':
|
|
246
|
-
case 'GN_SPEAKING_INTERVIEW':
|
|
247
234
|
case 'SPEAKING_CUE_CARD': {
|
|
248
235
|
return (_jsx(SpeakingQuestionRenderer, { part: part, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode, onExit: onExit, onPartComplete: onPartComplete, onPartBack: onPartBack, speakingTheme: speakingTheme, initialQuestionIndex: initialQuestionIndex, skipTeacherVideo: skipTeacherVideo, onTeacherIntroPlayed: onTeacherIntroPlayed, currentQuestionIndex: currentQuestionIndex }));
|
|
249
236
|
}
|
|
@@ -3,11 +3,8 @@ export { MoversColoringRenderer } from './MoversColoringRenderer';
|
|
|
3
3
|
export { MoversChooseBestAnswerRenderer } from './MoversChooseBestAnswerRenderer';
|
|
4
4
|
export { MoversChooseAdjectiveRenderer } from './MoversChooseAdjectiveRenderer';
|
|
5
5
|
export { MoversWritingRenderer } from './MoversWritingRenderer';
|
|
6
|
-
export { MoversPictureChooseRenderer } from './MoversPictureChooseRenderer';
|
|
7
|
-
export { MoversPictureFillBlankChooseRenderer } from './MoversPictureFillBlankChooseRenderer';
|
|
8
6
|
export { MoversReadingPassageRenderer } from './MoversReadingPassageRenderer';
|
|
9
7
|
export { MoversLabelThePictureRenderer } from './MoversLabelThePictureRenderer';
|
|
10
|
-
export { MoversFillWordHintRenderer } from './MoversFillWordHintRenderer';
|
|
11
8
|
export { MoversGridFillRenderer } from './MoversGridFillRenderer';
|
|
12
9
|
export { MoversTrueFalseRenderer } from './MoversTrueFalseRenderer';
|
|
13
10
|
export { MoversTrueFalseCorrectGroupRenderer } from './MoversTrueFalseCorrectGroupRenderer';
|
|
@@ -4,12 +4,9 @@ export { MoversChooseBestAnswerRenderer } from './MoversChooseBestAnswerRenderer
|
|
|
4
4
|
export { MoversChooseAdjectiveRenderer } from './MoversChooseAdjectiveRenderer';
|
|
5
5
|
export { MoversWritingRenderer } from './MoversWritingRenderer';
|
|
6
6
|
// New renderers - P1 (Simple)
|
|
7
|
-
export { MoversPictureChooseRenderer } from './MoversPictureChooseRenderer';
|
|
8
|
-
export { MoversPictureFillBlankChooseRenderer } from './MoversPictureFillBlankChooseRenderer';
|
|
9
7
|
export { MoversReadingPassageRenderer } from './MoversReadingPassageRenderer';
|
|
10
8
|
// New renderers - P2 (Medium)
|
|
11
9
|
export { MoversLabelThePictureRenderer } from './MoversLabelThePictureRenderer';
|
|
12
|
-
export { MoversFillWordHintRenderer } from './MoversFillWordHintRenderer';
|
|
13
10
|
// New renderers - P3 (Complex)
|
|
14
11
|
export { MoversGridFillRenderer } from './MoversGridFillRenderer';
|
|
15
12
|
// Starters & Movers renderers
|
package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js
CHANGED
|
@@ -17,7 +17,6 @@ import { SpeakingSpontaneousQaGroupRenderer } from './SpeakingSpontaneousQaGroup
|
|
|
17
17
|
import { SpeakingSpontaneousQaGroupTextList } from './SpeakingSpontaneousQaGroupTextList';
|
|
18
18
|
import { isTextOnlySpontaneousQaGroupContent } from '../../../../../questions/types/spontaneous-qa-group/flatten';
|
|
19
19
|
import { SpeakingConversationRenderer } from './SpeakingConversationRenderer';
|
|
20
|
-
import { SpeakingGNInterviewRenderer } from './SpeakingGNInterviewRenderer';
|
|
21
20
|
import { SpeakingCueCardRenderer } from './SpeakingCueCardRenderer';
|
|
22
21
|
// ============================================
|
|
23
22
|
// Factory
|
|
@@ -86,9 +85,6 @@ export function SpeakingQuestionRenderer({ part, answers, onAnswerChange, isRevi
|
|
|
86
85
|
// KFS Speaking — 3-participant conversation (Interlocutor + Partner + Student)
|
|
87
86
|
case 'SPEAKING_CONVERSATION':
|
|
88
87
|
return _jsx(SpeakingConversationRenderer, { ...sharedProps });
|
|
89
|
-
// GN Speaking Interview — virtual + real candidate simulation
|
|
90
|
-
case 'GN_SPEAKING_INTERVIEW':
|
|
91
|
-
return _jsx(SpeakingGNInterviewRenderer, { ...sharedProps });
|
|
92
88
|
// IELTS Part 2 — Cue Card (Preparation + Recording)
|
|
93
89
|
case 'SPEAKING_CUE_CARD':
|
|
94
90
|
return _jsx(SpeakingCueCardRenderer, { ...sharedProps });
|
|
@@ -81,37 +81,6 @@ export interface SpontaneousQAContent {
|
|
|
81
81
|
/** Backup flag — consistent with other speaking types */
|
|
82
82
|
isBackup?: boolean;
|
|
83
83
|
}
|
|
84
|
-
export interface GNSpeakingInterviewContent {
|
|
85
|
-
/** Examiner's question text */
|
|
86
|
-
questionText: string;
|
|
87
|
-
/** Audio URL for examiner's question (TTS or uploaded) */
|
|
88
|
-
audioUrl?: string;
|
|
89
|
-
/** How admin created the question */
|
|
90
|
-
inputType: 'AUDIO' | 'TEXT';
|
|
91
|
-
/** Which candidate this question is addressed to */
|
|
92
|
-
targetCandidate: 'VIRTUAL' | 'REAL';
|
|
93
|
-
/** Booklet image URL — collage for Part 2 Discussion with Pictures */
|
|
94
|
-
imageUrl?: string;
|
|
95
|
-
/** Virtual student's pre-recorded answer (filled by admin) */
|
|
96
|
-
virtualAnswer?: {
|
|
97
|
-
/** Text of the virtual student's answer */
|
|
98
|
-
answerText: string;
|
|
99
|
-
/** Audio URL — admin uploads or auto-generates via TTS */
|
|
100
|
-
audioUrl?: string;
|
|
101
|
-
};
|
|
102
|
-
/** Whether this is an Extended Response question */
|
|
103
|
-
isExtendedResponse?: boolean;
|
|
104
|
-
/** Backup flag */
|
|
105
|
-
isBackup?: boolean;
|
|
106
|
-
}
|
|
107
|
-
export interface GNSpeakingInterviewGroupPayload {
|
|
108
|
-
/** Which candidate role is virtual (pre-recorded) */
|
|
109
|
-
virtualCandidate: 'A' | 'B';
|
|
110
|
-
/** Virtual student display name */
|
|
111
|
-
virtualStudentName?: string;
|
|
112
|
-
/** Virtual student avatar image URL */
|
|
113
|
-
virtualStudentAvatar?: string;
|
|
114
|
-
}
|
|
115
84
|
/** Answer stored in examTakingStore for speaking questions */
|
|
116
85
|
export interface SpeakingAnswer {
|
|
117
86
|
audioUrl: string;
|
|
@@ -168,52 +168,6 @@ export interface ArticleItem {
|
|
|
168
168
|
content: string;
|
|
169
169
|
imageUrl?: string;
|
|
170
170
|
}
|
|
171
|
-
export interface PictureChooseOption {
|
|
172
|
-
id: string;
|
|
173
|
-
label: string;
|
|
174
|
-
text: string;
|
|
175
|
-
}
|
|
176
|
-
export interface PictureChooseSubQuestion {
|
|
177
|
-
id: string;
|
|
178
|
-
questionNumber: number;
|
|
179
|
-
imageUrl: string;
|
|
180
|
-
questionText?: string;
|
|
181
|
-
options: PictureChooseOption[];
|
|
182
|
-
correctOptionId?: string;
|
|
183
|
-
}
|
|
184
|
-
export interface MoversPictureChooseRendererProps {
|
|
185
|
-
partNumber: number;
|
|
186
|
-
partName?: string;
|
|
187
|
-
questionCount: number;
|
|
188
|
-
instruction: string;
|
|
189
|
-
subQuestions: PictureChooseSubQuestion[];
|
|
190
|
-
answers: Record<string, string>;
|
|
191
|
-
onAnswerChange: (subQuestionId: string, optionId: string) => void;
|
|
192
|
-
isReviewMode?: boolean;
|
|
193
|
-
}
|
|
194
|
-
export interface PictureFillBlankOption {
|
|
195
|
-
id: string;
|
|
196
|
-
label: string;
|
|
197
|
-
text: string;
|
|
198
|
-
}
|
|
199
|
-
export interface PictureFillBlankSubQuestion {
|
|
200
|
-
id: string;
|
|
201
|
-
questionNumber: number;
|
|
202
|
-
imageUrl: string;
|
|
203
|
-
question: string;
|
|
204
|
-
options: PictureFillBlankOption[];
|
|
205
|
-
correctOptionId?: string;
|
|
206
|
-
}
|
|
207
|
-
export interface MoversPictureFillBlankChooseRendererProps {
|
|
208
|
-
partNumber: number;
|
|
209
|
-
partName?: string;
|
|
210
|
-
questionCount: number;
|
|
211
|
-
instruction: string;
|
|
212
|
-
subQuestions: PictureFillBlankSubQuestion[];
|
|
213
|
-
answers: Record<string, string>;
|
|
214
|
-
onAnswerChange: (subQuestionId: string, optionId: string) => void;
|
|
215
|
-
isReviewMode?: boolean;
|
|
216
|
-
}
|
|
217
171
|
export interface ReadingPassageOption {
|
|
218
172
|
id: string;
|
|
219
173
|
label: string;
|
|
@@ -258,25 +212,6 @@ export interface MoversLabelThePictureRendererProps {
|
|
|
258
212
|
onAnswerChange: (labelId: string, value: string) => void;
|
|
259
213
|
isReviewMode?: boolean;
|
|
260
214
|
}
|
|
261
|
-
export interface FillWordHintSubQuestion {
|
|
262
|
-
id: string;
|
|
263
|
-
questionNumber: number;
|
|
264
|
-
imageUrl: string;
|
|
265
|
-
word: string;
|
|
266
|
-
correctAnswer?: string;
|
|
267
|
-
blankIndex?: number;
|
|
268
|
-
}
|
|
269
|
-
export interface MoversFillWordHintRendererProps {
|
|
270
|
-
partNumber: number;
|
|
271
|
-
partName?: string;
|
|
272
|
-
questionCount: number;
|
|
273
|
-
instruction: string;
|
|
274
|
-
hintLetters: string[];
|
|
275
|
-
subQuestions: FillWordHintSubQuestion[];
|
|
276
|
-
answers: Record<string, string>;
|
|
277
|
-
onAnswerChange: (subQuestionId: string, value: string) => void;
|
|
278
|
-
isReviewMode?: boolean;
|
|
279
|
-
}
|
|
280
215
|
export interface GridCellAnswer {
|
|
281
216
|
correctAnswer: string;
|
|
282
217
|
}
|
|
@@ -162,34 +162,6 @@ function findColoringParentQuestion(regionId, parts) {
|
|
|
162
162
|
}
|
|
163
163
|
return null;
|
|
164
164
|
}
|
|
165
|
-
/**
|
|
166
|
-
* Find parent question for sub-questions (LOOK_PICTURE_FILL_WORD_HINT)
|
|
167
|
-
* Supports both old format (content.subQuestions) and new format (questionId-blank-N)
|
|
168
|
-
*/
|
|
169
|
-
function findFillWordHintParentQuestion(subQuestionId, parts) {
|
|
170
|
-
for (const part of parts) {
|
|
171
|
-
if (part.questionType === 'LOOK_PICTURE_FILL_WORD_HINT') {
|
|
172
|
-
for (const question of part.questions) {
|
|
173
|
-
// NEW FORMAT: Check if subQuestionId matches pattern "questionId-blank-N"
|
|
174
|
-
if (subQuestionId.startsWith(`${question.id}-blank-`)) {
|
|
175
|
-
return {
|
|
176
|
-
parentId: question.id,
|
|
177
|
-
questionType: part.questionType,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
// OLD FORMAT: Check subQuestions array in content
|
|
181
|
-
const content = question.content;
|
|
182
|
-
if (content?.subQuestions?.some((sq) => sq.id === subQuestionId)) {
|
|
183
|
-
return {
|
|
184
|
-
parentId: question.id,
|
|
185
|
-
questionType: part.questionType,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return null;
|
|
192
|
-
}
|
|
193
165
|
/**
|
|
194
166
|
* Find question type by questionId
|
|
195
167
|
*/
|
|
@@ -327,14 +299,9 @@ export function transformAnswersForSubmission(answers, parts) {
|
|
|
327
299
|
}
|
|
328
300
|
continue;
|
|
329
301
|
}
|
|
330
|
-
// Handle synthetic sub-question IDs (from READ_PASSAGE_AND_ANSWER_QUESTIONS
|
|
302
|
+
// Handle synthetic sub-question IDs (from READ_PASSAGE_AND_ANSWER_QUESTIONS)
|
|
331
303
|
if (isSyntheticSubQuestionId(questionId)) {
|
|
332
|
-
|
|
333
|
-
let parent = findParentQuestion(questionId, parts);
|
|
334
|
-
// If not found, try LOOK_PICTURE_FILL_WORD_HINT
|
|
335
|
-
if (!parent) {
|
|
336
|
-
parent = findFillWordHintParentQuestion(questionId, parts);
|
|
337
|
-
}
|
|
304
|
+
const parent = findParentQuestion(questionId, parts);
|
|
338
305
|
if (parent) {
|
|
339
306
|
if (!subQuestionAnswers.has(parent.parentId)) {
|
|
340
307
|
subQuestionAnswers.set(parent.parentId, {});
|
|
@@ -457,31 +424,10 @@ export function transformAnswersForSubmission(answers, parts) {
|
|
|
457
424
|
});
|
|
458
425
|
continue;
|
|
459
426
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
// Sort by blank index and join
|
|
465
|
-
const sortedEntries = Object.entries(subAnswers).sort(([a], [b]) => {
|
|
466
|
-
const aMatch = a.match(/-blank-(\d+)$/);
|
|
467
|
-
const bMatch = b.match(/-blank-(\d+)$/);
|
|
468
|
-
const aIdx = aMatch ? parseInt(aMatch[1], 10) : 0;
|
|
469
|
-
const bIdx = bMatch ? parseInt(bMatch[1], 10) : 0;
|
|
470
|
-
return aIdx - bIdx;
|
|
471
|
-
});
|
|
472
|
-
const commaSeparated = sortedEntries.map(([, v]) => v).join(', ');
|
|
473
|
-
result.push({
|
|
474
|
-
questionId: parentId,
|
|
475
|
-
answer: { answer: commaSeparated }, // Simple format: { answer: "a, e" }
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
else {
|
|
479
|
-
// OLD FORMAT or other question types: keep subAnswers format
|
|
480
|
-
result.push({
|
|
481
|
-
questionId: parentId,
|
|
482
|
-
answer: { subAnswers },
|
|
483
|
-
});
|
|
484
|
-
}
|
|
427
|
+
result.push({
|
|
428
|
+
questionId: parentId,
|
|
429
|
+
answer: { subAnswers },
|
|
430
|
+
});
|
|
485
431
|
}
|
|
486
432
|
// Add grid cell answers (grouped under parent question)
|
|
487
433
|
for (const [parentId, cellAnswers] of gridCellAnswers) {
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Maps backend question types to frontend renderer props.
|
|
5
5
|
* Each transformer handles specific question type data structure.
|
|
6
6
|
*/
|
|
7
|
-
import type { FillBlankQuestion, ChooseBestAnswerQuestion,
|
|
8
|
-
export type QuestionType = 'FILL_IN_BLANK' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'CHOOSE_CORRECT_ADJECTIVE' | 'WRITE_A_SHORT_LETTER' | 'READ_AND_COLOR_OBJECTS' | '
|
|
7
|
+
import type { FillBlankQuestion, ChooseBestAnswerQuestion, ReadingPassageQuestion, LabelThePictureLabel, ColoringHintItem, ColoringRegion } from '../types';
|
|
8
|
+
export type QuestionType = 'FILL_IN_BLANK' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'CHOOSE_CORRECT_ADJECTIVE' | 'WRITE_A_SHORT_LETTER' | 'READ_AND_COLOR_OBJECTS' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'LABEL_THE_PICTURE' | 'FILL_MISSING_WORDS_IN_GRID' | 'IMAGE_OBJECT_MATCHING' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'TRUE_FALSE' | 'TRUE_FALSE_GROUP' | 'TRUE_FALSE_CORRECT_GROUP' | 'FILL_IN_BLANK_GROUP' | 'MATCHING_WITH_LINES_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'WORD_ORDERING' | 'WORD_ORDERING_GROUP' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'MATCH_WORD_TO_PICTURE' | 'WRITE_SENTENCES' | 'WORD_FILL_STRUCTURED_FORM' | 'CROSSWORD_PUZZLE' | 'FIND_WORDS_IN_MATRIX' | 'SORT_WORDS_INTO_CATEGORIES' | 'CHOOSE_THEN_ANSWER_GROUP' | 'WORD_ORDER_AND_MATCH_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPONTANEOUS_QA_GROUP' | 'SPEAKING_CONVERSATION' | 'SPEAKING_CUE_CARD';
|
|
9
9
|
export interface ApiQuestion {
|
|
10
10
|
id: string;
|
|
11
11
|
content: any;
|
|
@@ -204,14 +204,6 @@ export declare function transformColorObjects(questions: ApiQuestion[]): {
|
|
|
204
204
|
hintItems: ColoringHintItem[];
|
|
205
205
|
regions: ColoringRegion[];
|
|
206
206
|
};
|
|
207
|
-
export declare function transformPictureChoose(questions: ApiQuestion[]): {
|
|
208
|
-
subQuestions: PictureChooseSubQuestion[];
|
|
209
|
-
instruction: string;
|
|
210
|
-
};
|
|
211
|
-
export declare function transformPictureFillBlankChoose(questions: ApiQuestion[]): {
|
|
212
|
-
subQuestions: PictureFillBlankSubQuestion[];
|
|
213
|
-
instruction: string;
|
|
214
|
-
};
|
|
215
207
|
export declare function transformReadingPassage(questions: ApiQuestion[]): {
|
|
216
208
|
passage: string;
|
|
217
209
|
passageTitle?: string;
|
|
@@ -225,11 +217,6 @@ export declare function transformLabelPicture(questions: ApiQuestion[]): {
|
|
|
225
217
|
labels: LabelThePictureLabel[];
|
|
226
218
|
instruction: string;
|
|
227
219
|
};
|
|
228
|
-
export declare function transformFillWordHint(questions: ApiQuestion[]): {
|
|
229
|
-
subQuestions: FillWordHintSubQuestion[];
|
|
230
|
-
hintLetters: string[];
|
|
231
|
-
instruction: string;
|
|
232
|
-
};
|
|
233
220
|
export declare function transformGridFill(questions: ApiQuestion[]): {
|
|
234
221
|
grid: (string | null)[][];
|
|
235
222
|
rows: number;
|
|
@@ -618,49 +618,6 @@ export function transformColorObjects(questions) {
|
|
|
618
618
|
regions,
|
|
619
619
|
};
|
|
620
620
|
}
|
|
621
|
-
export function transformPictureChoose(questions) {
|
|
622
|
-
const firstContent = questions[0]?.content;
|
|
623
|
-
const subQuestions = questions.map((q) => {
|
|
624
|
-
const content = q.content;
|
|
625
|
-
return {
|
|
626
|
-
id: q.id,
|
|
627
|
-
questionNumber: q.question_number,
|
|
628
|
-
imageUrl: content.imageUrl || '',
|
|
629
|
-
questionText: content.questionText,
|
|
630
|
-
options: (content.options || []).map((opt, idx) => ({
|
|
631
|
-
id: opt.id || `${q.id}-opt-${idx}`,
|
|
632
|
-
label: OPTION_LABELS[idx] || String(idx + 1),
|
|
633
|
-
text: typeof opt === 'string' ? opt : opt.text,
|
|
634
|
-
})),
|
|
635
|
-
};
|
|
636
|
-
});
|
|
637
|
-
return {
|
|
638
|
-
subQuestions,
|
|
639
|
-
instruction: firstContent?.instruction || 'LOOK AT THE PICTURE AND CHOOSE THE CORRECT ANSWER',
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
export function transformPictureFillBlankChoose(questions) {
|
|
643
|
-
const firstContent = questions[0]?.content;
|
|
644
|
-
const subQuestions = questions.map((q) => {
|
|
645
|
-
const content = q.content;
|
|
646
|
-
return {
|
|
647
|
-
id: q.id,
|
|
648
|
-
questionNumber: q.question_number,
|
|
649
|
-
imageUrl: content.imageUrl || '',
|
|
650
|
-
question: content.question || '',
|
|
651
|
-
options: (content.options || []).map((opt, idx) => ({
|
|
652
|
-
id: opt.id || `${q.id}-opt-${idx}`,
|
|
653
|
-
label: OPTION_LABELS[idx] || String(idx + 1),
|
|
654
|
-
text: typeof opt === 'string' ? opt : opt.text,
|
|
655
|
-
})),
|
|
656
|
-
};
|
|
657
|
-
});
|
|
658
|
-
return {
|
|
659
|
-
subQuestions,
|
|
660
|
-
instruction: firstContent?.instruction ||
|
|
661
|
-
'LOOK AT THE PICTURE, FILL IN THE BLANK AND CHOOSE THE ANSWER',
|
|
662
|
-
};
|
|
663
|
-
}
|
|
664
621
|
/**
|
|
665
622
|
* Detect if data uses new format (separate questions with questionGroup)
|
|
666
623
|
* New format: content has 'question' field (not 'questions' array)
|
|
@@ -737,106 +694,6 @@ export function transformLabelPicture(questions) {
|
|
|
737
694
|
instruction: content?.instruction || 'LABEL THE PICTURE',
|
|
738
695
|
};
|
|
739
696
|
}
|
|
740
|
-
/**
|
|
741
|
-
* Detect if content uses new format (single word with {n} placeholders)
|
|
742
|
-
*/
|
|
743
|
-
function isNewFillWordHintFormat(content) {
|
|
744
|
-
return content && typeof content.word === 'string' && !content.subQuestions;
|
|
745
|
-
}
|
|
746
|
-
/**
|
|
747
|
-
* Parse word string with numbered placeholders like "c{0}t, {1}lephan"
|
|
748
|
-
* Returns array of parsed blanks with their segments
|
|
749
|
-
*/
|
|
750
|
-
function parseNumberedPlaceholders(word) {
|
|
751
|
-
const results = [];
|
|
752
|
-
// Split by comma to handle multiple words like "c{0}t, {1}lephan"
|
|
753
|
-
const wordSegments = word.split(',').map((s) => s.trim());
|
|
754
|
-
for (const segment of wordSegments) {
|
|
755
|
-
// Find all placeholders in this segment
|
|
756
|
-
let match;
|
|
757
|
-
const segmentRegex = /\{(\d+)\}/g;
|
|
758
|
-
while ((match = segmentRegex.exec(segment)) !== null) {
|
|
759
|
-
const placeholderIndex = parseInt(match[1], 10);
|
|
760
|
-
// Replace {n} with _ for display
|
|
761
|
-
const displayWord = segment.replace(match[0], '_');
|
|
762
|
-
results.push({
|
|
763
|
-
index: placeholderIndex,
|
|
764
|
-
displayWord,
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
// Sort by index to ensure correct order
|
|
769
|
-
results.sort((a, b) => a.index - b.index);
|
|
770
|
-
return results;
|
|
771
|
-
}
|
|
772
|
-
export function transformFillWordHint(questions) {
|
|
773
|
-
const firstQuestion = questions[0];
|
|
774
|
-
const content = firstQuestion?.content;
|
|
775
|
-
// MULTI-QUESTION FORMAT: Multiple separate questions with same group_id
|
|
776
|
-
// Each has own id, imageUrl, and single {0} placeholder
|
|
777
|
-
if (questions.length > 1 && questions.every((q) => isNewFillWordHintFormat(q.content))) {
|
|
778
|
-
const hintLetters = firstQuestion.questionGroup?.payload?.hintLetters || [];
|
|
779
|
-
const subQuestions = questions.map((q) => {
|
|
780
|
-
const qContent = q.content;
|
|
781
|
-
// Replace {0} with underscore for display
|
|
782
|
-
const displayWord = qContent.word.replace(/\{\d+\}/, '_');
|
|
783
|
-
return {
|
|
784
|
-
id: q.id, // Use actual question ID (not synthetic)
|
|
785
|
-
questionNumber: q.question_number,
|
|
786
|
-
imageUrl: qContent.imageUrl || '',
|
|
787
|
-
word: displayWord,
|
|
788
|
-
// No blankIndex → triggers OLD FORMAT rendering (grid with individual images)
|
|
789
|
-
};
|
|
790
|
-
});
|
|
791
|
-
return {
|
|
792
|
-
subQuestions,
|
|
793
|
-
hintLetters,
|
|
794
|
-
instruction: content?.instruction ||
|
|
795
|
-
'LOOK AT THE PICTURE AND FILL IN THE MISSING LETTER',
|
|
796
|
-
};
|
|
797
|
-
}
|
|
798
|
-
// Detect format and transform accordingly
|
|
799
|
-
if (isNewFillWordHintFormat(content)) {
|
|
800
|
-
// NEW FORMAT: Single word with {0}, {1} placeholders
|
|
801
|
-
const newContent = content;
|
|
802
|
-
// Get hintLetters from questionGroup.payload (new location)
|
|
803
|
-
const hintLetters = firstQuestion.questionGroup?.payload?.hintLetters || [];
|
|
804
|
-
// Parse the word string to extract placeholders
|
|
805
|
-
const placeholders = parseNumberedPlaceholders(newContent.word);
|
|
806
|
-
// Create sub-questions for each placeholder
|
|
807
|
-
const subQuestions = placeholders.map((p) => ({
|
|
808
|
-
id: `${firstQuestion.id}-blank-${p.index}`,
|
|
809
|
-
questionNumber: firstQuestion.question_number,
|
|
810
|
-
imageUrl: newContent.imageUrl || '',
|
|
811
|
-
word: p.displayWord,
|
|
812
|
-
blankIndex: p.index,
|
|
813
|
-
}));
|
|
814
|
-
return {
|
|
815
|
-
subQuestions,
|
|
816
|
-
hintLetters,
|
|
817
|
-
instruction: newContent.instruction ||
|
|
818
|
-
'LOOK AT THE PICTURE AND FILL IN THE MISSING LETTER',
|
|
819
|
-
};
|
|
820
|
-
}
|
|
821
|
-
// OLD FORMAT: subQuestions array with individual images
|
|
822
|
-
const oldContent = content;
|
|
823
|
-
const subQuestions = oldContent?.subQuestions?.map((sq, idx) => ({
|
|
824
|
-
id: sq.id,
|
|
825
|
-
questionNumber: firstQuestion.question_number + idx,
|
|
826
|
-
imageUrl: sq.imageUrl || '',
|
|
827
|
-
word: sq.word || '',
|
|
828
|
-
})) || [];
|
|
829
|
-
// Try hintLetters from content first, then from questionGroup.payload
|
|
830
|
-
const hintLetters = oldContent?.hintLetters ||
|
|
831
|
-
firstQuestion.questionGroup?.payload?.hintLetters ||
|
|
832
|
-
[];
|
|
833
|
-
return {
|
|
834
|
-
subQuestions,
|
|
835
|
-
hintLetters,
|
|
836
|
-
instruction: oldContent?.instruction ||
|
|
837
|
-
'LOOK AT THE PICTURE AND FILL IN THE MISSING LETTER',
|
|
838
|
-
};
|
|
839
|
-
}
|
|
840
697
|
export function transformGridFill(questions) {
|
|
841
698
|
const firstQuestion = questions[0];
|
|
842
699
|
const content = firstQuestion?.content;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
interface CompactCreatorHeaderProps {
|
|
3
|
+
icon: ReactNode;
|
|
4
|
+
title: string;
|
|
5
|
+
points?: {
|
|
6
|
+
value: number;
|
|
7
|
+
onValueChange: (value: number) => void;
|
|
8
|
+
error?: string;
|
|
9
|
+
allowZero?: boolean;
|
|
10
|
+
};
|
|
11
|
+
example?: {
|
|
12
|
+
checked: boolean;
|
|
13
|
+
onChange: (checked: boolean) => void;
|
|
14
|
+
};
|
|
15
|
+
preview?: {
|
|
16
|
+
onClick: () => void;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
};
|
|
19
|
+
headerExtra?: ReactNode;
|
|
20
|
+
extraActions?: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
export declare function CompactCreatorHeader({ icon, title, points, example, preview, headerExtra, extraActions, }: CompactCreatorHeaderProps): import("react").JSX.Element;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { CardTitle } from '../../../../../components/ui/card';
|
|
4
|
+
import { PointsInput } from '../../../../../components/ui/points-input';
|
|
5
|
+
import { CompactExampleToggle } from './CompactExampleToggle';
|
|
6
|
+
import { CompactPreviewButton } from './CompactPreviewButton';
|
|
7
|
+
export function CompactCreatorHeader({ icon, title, points, example, preview, headerExtra, extraActions, }) {
|
|
8
|
+
return (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100 text-gray-600", children: icon }), _jsx(CardTitle, { className: "text-sm font-bold text-gray-900", children: title })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [points && (_jsx(PointsInput, { value: points.value, onValueChange: points.onValueChange, error: points.error, allowZero: points.allowZero })), example && (_jsx(CompactExampleToggle, { checked: example.checked, onChange: example.onChange })), extraActions, headerExtra, preview && (_jsx(CompactPreviewButton, { onClick: preview.onClick, disabled: preview.disabled }))] })] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Star } from 'lucide-react';
|
|
4
|
+
export function CompactExampleToggle({ checked, onChange }) {
|
|
5
|
+
return (_jsxs("button", { type: "button", onClick: () => onChange(!checked), className: `inline-flex h-8 items-center gap-1.5 rounded-md border px-2.5 text-xs font-medium transition-colors select-none ${checked
|
|
6
|
+
? 'border-amber-300 bg-amber-50 text-amber-700'
|
|
7
|
+
: 'border-gray-200 bg-white text-gray-400 hover:text-gray-600'}`, children: [_jsx(Star, { className: `h-3.5 w-3.5 ${checked ? 'fill-amber-400 text-amber-400' : ''}` }), "Example"] }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
interface CompactExplanationToggleProps {
|
|
2
|
+
value: string;
|
|
3
|
+
onValueChange: (value: string) => void;
|
|
4
|
+
defaultVisible?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function CompactExplanationToggle({ value, onValueChange, defaultVisible, }: CompactExplanationToggleProps): import("react").JSX.Element;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { Button } from '../../../../../components/ui/button';
|
|
5
|
+
import { Textarea } from '../../../../../components/ui/textarea';
|
|
6
|
+
import { Label } from '../../../../../components/ui/label';
|
|
7
|
+
import { BookOpen } from 'lucide-react';
|
|
8
|
+
export function CompactExplanationToggle({ value, onValueChange, defaultVisible, }) {
|
|
9
|
+
const [visible, setVisible] = useState(() => defaultVisible ?? Boolean(value));
|
|
10
|
+
if (!visible) {
|
|
11
|
+
return (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setVisible(true), className: "h-8 gap-1.5 border-amber-200 px-2.5 text-xs text-amber-700 hover:bg-amber-50 hover:text-amber-800", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5" }), value ? 'Hiện giải thích' : 'Thêm giải thích'] }));
|
|
12
|
+
}
|
|
13
|
+
return (_jsxs("div", { className: "relative pr-10", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setVisible(false), className: "absolute right-0 top-0 z-10 h-7 px-2 text-xs text-gray-500 hover:text-gray-700", children: "\u1EA8n" }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx(Textarea, { rows: 2, value: value, onChange: (event) => onValueChange(event.target.value), placeholder: "Nh\u1EADp gi\u1EA3i th\u00EDch (t\u00F9y ch\u1ECDn)", className: "min-h-[56px] border-gray-200" })] })] }));
|
|
14
|
+
}
|