@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.3

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 (514) hide show
  1. package/dist/api/exam-create.js +1 -179
  2. package/dist/api/exam-create.types.js +1 -1
  3. package/dist/api/exam-questions.d.ts +1 -1
  4. package/dist/api/exam-questions.js +1 -98
  5. package/dist/api/exam-questions.types.d.ts +45 -1
  6. package/dist/api/exam-questions.types.js +1 -1
  7. package/dist/api/exam-taking.js +1 -73
  8. package/dist/api/exam-taking.types.js +1 -1
  9. package/dist/api/index.js +1 -6
  10. package/dist/api/result-review.js +1 -50
  11. package/dist/api/result-review.types.js +1 -1
  12. package/dist/components/CueCardEditor.js +1 -58
  13. package/dist/components/common/ResolvedImage.js +1 -15
  14. package/dist/components/exams/CompletionDialog.d.ts +8 -0
  15. package/dist/components/exams/CompletionDialog.js +1 -0
  16. package/dist/components/exams/CreateExamInfoForm.d.ts +4 -1
  17. package/dist/components/exams/CreateExamInfoForm.js +1 -35
  18. package/dist/components/exams/CreateExamPageContainer.d.ts +3 -0
  19. package/dist/components/exams/CreateExamPageContainer.js +1 -103
  20. package/dist/components/exams/ExamCreator.d.ts +9 -3
  21. package/dist/components/exams/ExamCreator.js +1 -89
  22. package/dist/components/exams/ExamPartTabs.d.ts +1 -1
  23. package/dist/components/exams/ExamPartTabs.js +1 -40
  24. package/dist/components/exams/ExamPreviewDialog.d.ts +20 -0
  25. package/dist/components/exams/ExamPreviewDialog.js +1 -0
  26. package/dist/components/exams/ExamQuestionGrid.d.ts +1 -1
  27. package/dist/components/exams/ExamQuestionGrid.js +1 -18
  28. package/dist/components/exams/ExamQuestionsPageContainer.d.ts +3 -0
  29. package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
  30. package/dist/components/exams/ExamViewDialog.d.ts +24 -0
  31. package/dist/components/exams/ExamViewDialog.js +1 -0
  32. package/dist/components/exams/PartAudioUploader.js +1 -9
  33. package/dist/components/exams/TemplateSelector.js +1 -66
  34. package/dist/components/exams/UnsavedChangesDialog.d.ts +8 -0
  35. package/dist/components/exams/UnsavedChangesDialog.js +1 -0
  36. package/dist/components/exams/api.d.ts +1 -1
  37. package/dist/components/exams/api.js +1 -3
  38. package/dist/components/exams/create-exam.texts.d.ts +58 -0
  39. package/dist/components/exams/create-exam.texts.js +1 -0
  40. package/dist/components/exams/create-exam.validation.js +1 -18
  41. package/dist/components/exams/exam-question-status.d.ts +9 -0
  42. package/dist/components/exams/exam-question-status.js +1 -0
  43. package/dist/components/exams/exam-template.utils.js +1 -18
  44. package/dist/components/exams/index.d.ts +11 -1
  45. package/dist/components/exams/index.js +1 -10
  46. package/dist/components/exams/take/ExamTakingPageContainer.js +1 -153
  47. package/dist/components/exams/take/GenericQuestionRenderer.js +1 -14
  48. package/dist/components/exams/take/exam-taking.utils.js +1 -88
  49. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -24
  50. package/dist/components/exams/take/index.js +1 -2
  51. package/dist/components/exams/take/types.js +1 -1
  52. package/dist/components/index.js +1 -42
  53. package/dist/components/questions/_shared/config/question-types.config.js +1 -333
  54. package/dist/components/questions/_shared/constants/color-palette.js +1 -21
  55. package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +1 -105
  56. package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +1 -222
  57. package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +1 -140
  58. package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +1 -123
  59. package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +1 -50
  60. package/dist/components/questions/_shared/hooks/useQuestionFormState.js +1 -54
  61. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +1 -123
  62. package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +1 -121
  63. package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +1 -70
  64. package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +1 -194
  65. package/dist/components/questions/_shared/index.js +1 -7
  66. package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
  67. package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
  68. package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +1 -3
  69. package/dist/components/questions/_shared/types/color-region.config.js +1 -1
  70. package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
  71. package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
  72. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
  73. package/dist/components/questions/_shared/types/index.js +1 -12
  74. package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
  75. package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
  76. package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
  77. package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
  78. package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
  79. package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
  80. package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
  81. package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
  82. package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +1 -3
  83. package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
  84. package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +1 -3
  85. package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
  86. package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
  87. package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
  88. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
  89. package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
  90. package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
  91. package/dist/components/questions/_shared/types/question-group.type.js +1 -11
  92. package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
  93. package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
  94. package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
  95. package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
  96. package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
  97. package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
  98. package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
  99. package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
  100. package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
  101. package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
  102. package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +1 -3
  103. package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
  104. package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
  105. package/dist/components/questions/_shared/utils/answer-builder.js +1 -79
  106. package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +1 -39
  107. package/dist/components/questions/_shared/utils/id-generator.js +1 -3
  108. package/dist/components/questions/_shared/utils/question-validation.js +1 -21
  109. package/dist/components/questions/components/ColorNode.js +1 -85
  110. package/dist/components/questions/components/ColorRegionClient.js +1 -545
  111. package/dist/components/questions/components/ColorRegionCreator.js +1 -420
  112. package/dist/components/questions/components/ColorRegionNode.js +1 -59
  113. package/dist/components/questions/components/CreatorGuide.js +1 -103
  114. package/dist/components/questions/components/NameNode.js +1 -26
  115. package/dist/components/questions/components/QuestionBankOption.js +1 -46
  116. package/dist/components/questions/components/QuestionBasicInfoForm.js +1 -9
  117. package/dist/components/questions/components/QuestionCommonFields.js +1 -33
  118. package/dist/components/questions/components/QuestionSearchDropdown.js +1 -258
  119. package/dist/components/questions/components/QuestionTypeSelector.js +1 -32
  120. package/dist/components/questions/components/QuestionTypeSpecificForms.js +1 -6
  121. package/dist/components/questions/components/RegionNode.js +1 -94
  122. package/dist/components/questions/components/index.js +1 -14
  123. package/dist/components/questions/creator/QuestionCreator.js +1 -90
  124. package/dist/components/questions/creator/QuestionGroupCreator.js +1 -119
  125. package/dist/components/questions/creator/dedicated-component-router.js +1 -79
  126. package/dist/components/questions/creator/index.js +1 -3
  127. package/dist/components/questions/creator/question-type-registry.js +1 -80
  128. package/dist/components/questions/groups/ArticlesGroupCard.js +1 -121
  129. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -50
  130. package/dist/components/questions/groups/DocumentGroupCard.js +1 -114
  131. package/dist/components/questions/groups/MatchAnswerGroupCard.js +1 -25
  132. package/dist/components/questions/index.js +1 -10
  133. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +1 -37
  134. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +1 -351
  135. package/dist/components/questions/types/answer-the-question/register.js +1 -67
  136. package/dist/components/questions/types/answer-the-question/transform.js +1 -36
  137. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +1 -141
  138. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +1 -118
  139. package/dist/components/questions/types/arrange-letters-into-words/index.js +1 -2
  140. package/dist/components/questions/types/arrange-letters-into-words/register.js +1 -4
  141. package/dist/components/questions/types/arrange-letters-into-words/transform.js +1 -24
  142. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -64
  143. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +1 -169
  144. package/dist/components/questions/types/choose-correct-adjective/index.js +1 -2
  145. package/dist/components/questions/types/choose-correct-adjective/register.js +1 -55
  146. package/dist/components/questions/types/choose-correct-adjective/transform.js +1 -16
  147. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -131
  148. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -581
  149. package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -2
  150. package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -101
  151. package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -43
  152. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +1 -275
  153. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -448
  154. package/dist/components/questions/types/fill-in-blank/index.js +1 -2
  155. package/dist/components/questions/types/fill-in-blank/register.js +1 -138
  156. package/dist/components/questions/types/fill-in-blank/transform.js +1 -59
  157. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +1 -117
  158. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +1 -671
  159. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +1 -2
  160. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +1 -37
  161. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +1 -15
  162. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +1 -262
  163. package/dist/components/questions/types/gn-speaking-interview/register.js +1 -121
  164. package/dist/components/questions/types/gn-speaking-interview/transform.js +1 -48
  165. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +1 -16
  166. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +1 -27
  167. package/dist/components/questions/types/image-object-matching/index.js +1 -2
  168. package/dist/components/questions/types/image-object-matching/register.js +1 -3
  169. package/dist/components/questions/types/image-object-matching/transform.js +1 -2
  170. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +1 -102
  171. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +1 -344
  172. package/dist/components/questions/types/label-the-picture/index.js +1 -2
  173. package/dist/components/questions/types/label-the-picture/register.js +1 -54
  174. package/dist/components/questions/types/label-the-picture/transform.js +1 -28
  175. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +1 -78
  176. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +1 -232
  177. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +1 -2
  178. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +1 -82
  179. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +1 -27
  180. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +1 -70
  181. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +1 -386
  182. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +1 -2
  183. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +1 -100
  184. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +1 -33
  185. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +1 -106
  186. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +1 -457
  187. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +1 -2
  188. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +1 -110
  189. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +1 -35
  190. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +1 -8
  191. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -22
  192. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +1 -291
  193. package/dist/components/questions/types/listen-and-speak-answer/index.js +1 -2
  194. package/dist/components/questions/types/listen-and-speak-answer/register.js +1 -84
  195. package/dist/components/questions/types/listen-and-speak-answer/transform.js +1 -27
  196. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -32
  197. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +1 -295
  198. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +1 -79
  199. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +1 -27
  200. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -34
  201. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +1 -285
  202. package/dist/components/questions/types/listen-and-speak-image-group/register.js +1 -81
  203. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +1 -26
  204. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -38
  205. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +1 -385
  206. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +1 -87
  207. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +1 -32
  208. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -26
  209. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +1 -247
  210. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +1 -59
  211. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +1 -18
  212. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -26
  213. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +1 -256
  214. package/dist/components/questions/types/listen-and-speak-question-list/register.js +1 -81
  215. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +1 -26
  216. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -26
  217. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +1 -230
  218. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +1 -56
  219. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +1 -16
  220. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +1 -72
  221. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +1 -230
  222. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  223. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +1 -66
  224. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +1 -42
  225. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +1 -96
  226. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +1 -238
  227. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +1 -2
  228. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +1 -76
  229. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +1 -19
  230. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +1 -134
  231. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +1 -617
  232. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +1 -2
  233. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +1 -82
  234. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +1 -29
  235. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +1 -38
  236. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +1 -359
  237. package/dist/components/questions/types/match-by-writing-answer/index.js +1 -3
  238. package/dist/components/questions/types/match-by-writing-answer/register.js +1 -58
  239. package/dist/components/questions/types/match-by-writing-answer/transform.js +1 -61
  240. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +1 -16
  241. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +1 -23
  242. package/dist/components/questions/types/read-and-color-objects/index.js +1 -2
  243. package/dist/components/questions/types/read-and-color-objects/register.js +1 -3
  244. package/dist/components/questions/types/read-and-color-objects/transform.js +1 -36
  245. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +1 -125
  246. package/dist/components/questions/types/read-displayed-content/register.js +1 -64
  247. package/dist/components/questions/types/read-displayed-content/transform.js +1 -12
  248. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -64
  249. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +1 -511
  250. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +1 -2
  251. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +1 -58
  252. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +1 -28
  253. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +1 -373
  254. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +1 -41
  255. package/dist/components/questions/types/speaking-conversation/register.js +1 -110
  256. package/dist/components/questions/types/speaking-conversation/transform.js +1 -34
  257. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +1 -74
  258. package/dist/components/questions/types/speaking-cue-card/register.js +1 -52
  259. package/dist/components/questions/types/speaking-cue-card/transform.js +1 -12
  260. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -10
  261. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +1 -161
  262. package/dist/components/questions/types/speaking-describe-image/index.js +1 -2
  263. package/dist/components/questions/types/speaking-describe-image/register.js +1 -69
  264. package/dist/components/questions/types/speaking-describe-image/transform.js +1 -16
  265. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +1 -234
  266. package/dist/components/questions/types/spontaneous-qa/register.js +1 -93
  267. package/dist/components/questions/types/spontaneous-qa/transform.js +1 -34
  268. package/dist/components/questions/types/true-false/TrueFalseClient.js +1 -75
  269. package/dist/components/questions/types/true-false/TrueFalseCreator.js +1 -244
  270. package/dist/components/questions/types/true-false/index.js +1 -2
  271. package/dist/components/questions/types/true-false/register.js +1 -77
  272. package/dist/components/questions/types/true-false/transform.js +1 -32
  273. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +1 -233
  274. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +1 -631
  275. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +1 -28
  276. package/dist/components/questions/types/word-fill-paragraph/index.js +1 -2
  277. package/dist/components/questions/types/word-fill-paragraph/register.js +1 -136
  278. package/dist/components/questions/types/word-fill-paragraph/transform.js +1 -56
  279. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -138
  280. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -261
  281. package/dist/components/questions/types/word-fill-structured-form/index.js +1 -2
  282. package/dist/components/questions/types/word-fill-structured-form/register.js +1 -67
  283. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -40
  284. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -84
  285. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +1 -192
  286. package/dist/components/questions/types/word-ordering/index.js +1 -2
  287. package/dist/components/questions/types/word-ordering/register.js +1 -46
  288. package/dist/components/questions/types/word-ordering/transform.js +1 -24
  289. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -98
  290. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +1 -320
  291. package/dist/components/questions/types/write-a-short-letter/index.js +1 -2
  292. package/dist/components/questions/types/write-a-short-letter/register.js +1 -68
  293. package/dist/components/questions/types/write-a-short-letter/transform.js +1 -25
  294. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +1 -61
  295. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +1 -196
  296. package/dist/components/questions/types/write-correct-verb-form/index.js +1 -2
  297. package/dist/components/questions/types/write-correct-verb-form/register.js +1 -62
  298. package/dist/components/questions/types/write-correct-verb-form/transform.js +1 -17
  299. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +1 -22
  300. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +1 -174
  301. package/dist/components/questions/types/write-sentences/index.js +1 -2
  302. package/dist/components/questions/types/write-sentences/register.js +1 -58
  303. package/dist/components/questions/types/write-sentences/transform.js +1 -28
  304. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -96
  305. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +1 -161
  306. package/dist/components/questions/types/write-short-paragraph/index.js +1 -2
  307. package/dist/components/questions/types/write-short-paragraph/register.js +1 -4
  308. package/dist/components/questions/types/write-short-paragraph/transform.js +1 -21
  309. package/dist/components/questions/viewer/QuestionGroupViewer.js +1 -36
  310. package/dist/components/questions/viewer/QuestionViewer.js +1 -446
  311. package/dist/components/questions/viewer/index.js +1 -2
  312. package/dist/components/results/ResultReviewPageContainer.js +1 -98
  313. package/dist/components/results/ReviewQuestionRenderer.js +1 -72
  314. package/dist/components/results/index.js +1 -2
  315. package/dist/components/results/result-review.utils.js +1 -6
  316. package/dist/components/results/review-data.js +1 -150
  317. package/dist/components/results/review-renderer.utils.js +1 -97
  318. package/dist/components/results/review-types.js +1 -1
  319. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -138
  320. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +1 -12
  321. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +1 -18
  322. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +1 -11
  323. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +1 -11
  324. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +1 -9
  325. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +1 -13
  326. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +1 -8
  327. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +1 -6
  328. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +1 -5
  329. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +1 -9
  330. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -41
  331. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +1 -25
  332. package/dist/components/themes/cambridge-yle/index.js +1 -13
  333. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -24
  334. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -88
  335. package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -55
  336. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -156
  337. package/dist/components/themes/english-certification/EcWritingTask.js +1 -34
  338. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -114
  339. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +1 -11
  340. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -21
  341. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +1 -15
  342. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +1 -19
  343. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -148
  344. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +1 -31
  345. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +1 -71
  346. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +1 -33
  347. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +1 -16
  348. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -22
  349. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +1 -37
  350. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -72
  351. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -90
  352. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +1 -18
  353. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -29
  354. package/dist/components/themes/english-certification/english-certification-theme.js +1 -58
  355. package/dist/components/themes/english-certification/grouped-question-utils.js +1 -218
  356. package/dist/components/themes/english-certification/index.js +1 -21
  357. package/dist/components/themes/index.js +1 -6
  358. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +1 -170
  359. package/dist/components/themes/summer-sky/SummerSkyLayout.js +1 -221
  360. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +1 -18
  361. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +1 -194
  362. package/dist/components/themes/types.js +1 -1
  363. package/dist/components/ui/alert-dialog.js +1 -26
  364. package/dist/components/ui/alert.js +1 -22
  365. package/dist/components/ui/avatar.js +1 -12
  366. package/dist/components/ui/badge.js +1 -38
  367. package/dist/components/ui/button.js +1 -35
  368. package/dist/components/ui/calendar.js +1 -76
  369. package/dist/components/ui/card.js +1 -16
  370. package/dist/components/ui/carousel.js +1 -91
  371. package/dist/components/ui/checkbox.js +1 -9
  372. package/dist/components/ui/collapsible.js +1 -13
  373. package/dist/components/ui/command.js +1 -34
  374. package/dist/components/ui/confirm-dialog.js +1 -49
  375. package/dist/components/ui/dialog.js +1 -23
  376. package/dist/components/ui/drawer.js +1 -35
  377. package/dist/components/ui/dropdown-menu.js +1 -33
  378. package/dist/components/ui/file-upload.js +1 -155
  379. package/dist/components/ui/form.js +1 -62
  380. package/dist/components/ui/hover-card.js +1 -10
  381. package/dist/components/ui/image-preview.js +1 -29
  382. package/dist/components/ui/input.js +1 -8
  383. package/dist/components/ui/label.js +1 -9
  384. package/dist/components/ui/language-switcher.js +1 -15
  385. package/dist/components/ui/multi-select.js +1 -90
  386. package/dist/components/ui/points-input.js +1 -33
  387. package/dist/components/ui/popover.js +1 -26
  388. package/dist/components/ui/progress.js +1 -16
  389. package/dist/components/ui/radio-group.js +1 -12
  390. package/dist/components/ui/scroll-area.js +1 -11
  391. package/dist/components/ui/select.js +1 -38
  392. package/dist/components/ui/separator.js +1 -8
  393. package/dist/components/ui/sheet.js +1 -40
  394. package/dist/components/ui/single-select.js +1 -76
  395. package/dist/components/ui/skeleton.js +1 -6
  396. package/dist/components/ui/switch.js +1 -8
  397. package/dist/components/ui/table.js +1 -20
  398. package/dist/components/ui/tabs.js +1 -17
  399. package/dist/components/ui/textarea.js +1 -8
  400. package/dist/components/ui/tooltip.js +1 -11
  401. package/dist/index.d.ts +3 -3
  402. package/dist/index.js +1 -19
  403. package/dist/mcp/config.js +1 -32
  404. package/dist/mcp/http-client.js +1 -48
  405. package/dist/mcp/index.js +1 -29
  406. package/dist/mcp/tools/_helpers.js +1 -25
  407. package/dist/mcp/tools/exams.js +1 -439
  408. package/dist/mcp/tools/index.js +1 -6
  409. package/dist/mcp/tools/results.js +1 -19
  410. package/dist/shared/constants/ApiConstant.js +1 -276
  411. package/dist/shared/constants/QueryKeyConstant.js +1 -112
  412. package/dist/shared/constants/exam-level.enum.js +1 -41
  413. package/dist/shared/constants/landings/placement-test.constants.js +1 -431
  414. package/dist/shared/constants/question-skills.js +1 -267
  415. package/dist/shared/constants/routes.js +1 -81
  416. package/dist/shared/index.d.ts +3 -0
  417. package/dist/shared/index.js +1 -7
  418. package/dist/shared/lib/hooks/index.js +1 -10
  419. package/dist/shared/lib/hooks/useAlertDialog.js +1 -57
  420. package/dist/shared/lib/hooks/useAudioPlayCounter.js +1 -30
  421. package/dist/shared/lib/hooks/useAudioPreview.js +1 -81
  422. package/dist/shared/lib/hooks/useAudioUpload.js +1 -24
  423. package/dist/shared/lib/hooks/useConfirmDialog.js +1 -89
  424. package/dist/shared/lib/hooks/useDebouncedCallback.js +1 -32
  425. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +1 -12
  426. package/dist/shared/lib/hooks/useFileUpload.js +1 -24
  427. package/dist/shared/lib/hooks/useImageUrl.js +1 -5
  428. package/dist/shared/lib/hooks/usePresignedFileUrl.js +1 -10
  429. package/dist/shared/lib/hooks/useReactHookForm.js +1 -31
  430. package/dist/shared/lib/hooks/useToast.js +1 -62
  431. package/dist/shared/lib/i18n/index.js +1 -54
  432. package/dist/shared/lib/i18n/messages/en/admin.js +1 -2104
  433. package/dist/shared/lib/i18n/messages/en/auth.js +1 -86
  434. package/dist/shared/lib/i18n/messages/en/common.js +1 -140
  435. package/dist/shared/lib/i18n/messages/en/file-upload.js +1 -18
  436. package/dist/shared/lib/i18n/messages/en/student.js +1 -133
  437. package/dist/shared/lib/i18n/messages/en/terms.js +1 -37
  438. package/dist/shared/lib/i18n/messages/en.js +1 -15
  439. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -2104
  440. package/dist/shared/lib/i18n/messages/vi/auth.js +1 -86
  441. package/dist/shared/lib/i18n/messages/vi/common.js +1 -140
  442. package/dist/shared/lib/i18n/messages/vi/file-upload.js +1 -18
  443. package/dist/shared/lib/i18n/messages/vi/student.js +1 -133
  444. package/dist/shared/lib/i18n/messages/vi/terms.js +1 -37
  445. package/dist/shared/lib/i18n/messages/vi.js +1 -15
  446. package/dist/shared/lib/i18n/types.js +1 -1
  447. package/dist/shared/lib/stores/examQuestionStore.js +1 -212
  448. package/dist/shared/lib/stores/localeStore.js +1 -10
  449. package/dist/shared/lib/utils/public-page.js +1 -44
  450. package/dist/shared/lib/utils/question-reverse-transform.d.ts +6 -0
  451. package/dist/shared/lib/utils/question-reverse-transform.js +1 -0
  452. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  453. package/dist/shared/lib/utils/question-transform-validation.d.ts +6 -0
  454. package/dist/shared/lib/utils/question-transform-validation.js +1 -0
  455. package/dist/shared/lib/utils/question-transform.d.ts +6 -2
  456. package/dist/shared/lib/utils/question-transform.js +1 -174
  457. package/dist/shared/lib/utils.js +1 -168
  458. package/dist/shared/lib/validation/choose-correct-answer.validation.js +1 -111
  459. package/dist/shared/lib/validation/label-the-picture.validation.js +1 -19
  460. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +1 -46
  461. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +1 -21
  462. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +1 -34
  463. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +1 -40
  464. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +1 -45
  465. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +1 -19
  466. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +1 -39
  467. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +1 -32
  468. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +1 -19
  469. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +1 -28
  470. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +1 -12
  471. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +1 -47
  472. package/dist/shared/lib/validation/read-and-color-objects.validation.js +1 -29
  473. package/dist/shared/lib/validation/read-displayed-content.validation.js +1 -28
  474. package/dist/shared/lib/validation/speaking-conversation.validation.js +1 -75
  475. package/dist/shared/lib/validation/speaking-describe-image.validation.js +1 -31
  476. package/dist/shared/lib/validation/spontaneous-qa.validation.js +1 -48
  477. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +1 -38
  478. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +1 -39
  479. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +1 -43
  480. package/dist/shared/lib/validation/word-ordering.validation.js +1 -33
  481. package/dist/shared/types/branch.types.js +1 -6
  482. package/dist/shared/types/common.types.js +1 -50
  483. package/dist/shared/types/entities/exam-schedule.types.js +1 -5
  484. package/dist/shared/types/entities/exam.types.js +1 -1
  485. package/dist/shared/types/entities/question.types.js +1 -4
  486. package/dist/shared/types/entities/result.types.js +1 -4
  487. package/dist/shared/types/entities/user.types.js +1 -4
  488. package/dist/shared/types/exam-taking.types.js +1 -2
  489. package/dist/shared/types/history.types.js +1 -34
  490. package/dist/shared/types/index.js +1 -1
  491. package/dist/shared/types/questions/choose-correct-adjective.js +1 -4
  492. package/dist/shared/types/questions/choose-the-correct-answer.js +1 -9
  493. package/dist/shared/types/questions/fill-missing-words-in-grid.js +1 -11
  494. package/dist/shared/types/questions/index.js +1 -40
  495. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  496. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  497. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  498. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +1 -3
  499. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +1 -3
  500. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  501. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +1 -3
  502. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +1 -5
  503. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +1 -9
  504. package/dist/shared/types/questions/look-picture-fill-word-hint.js +1 -7
  505. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  506. package/dist/shared/types/questions/speaking-conversation.js +1 -7
  507. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  508. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  509. package/dist/shared/types/questions/write-correct-verb-form.js +1 -12
  510. package/dist/shared/types/role.types.js +1 -1
  511. package/dist/shared/types/structure-management.js +1 -28
  512. package/dist/shared/types/student.types.js +1 -54
  513. package/dist/shared/types/teacher-dashboard-rooms.js +1 -2
  514. package/package.json +12 -3
@@ -1,168 +1 @@
1
- import { clsx } from 'clsx';
2
- import { twMerge } from 'tailwind-merge';
3
- /**
4
- * Merge Tailwind CSS classes with clsx
5
- */
6
- export function cn(...inputs) {
7
- return twMerge(clsx(inputs));
8
- }
9
- /**
10
- * Format date to readable string
11
- */
12
- export function formatDate(date, format = 'DD/MM/YYYY') {
13
- const d = new Date(date);
14
- const day = String(d.getDate()).padStart(2, '0');
15
- const month = String(d.getMonth() + 1).padStart(2, '0');
16
- const year = d.getFullYear();
17
- const hours = String(d.getHours()).padStart(2, '0');
18
- const minutes = String(d.getMinutes()).padStart(2, '0');
19
- return format
20
- .replace('DD', day)
21
- .replace('MM', month)
22
- .replace('YYYY', String(year))
23
- .replace('HH', hours)
24
- .replace('mm', minutes);
25
- }
26
- /**
27
- * Format duration in seconds to readable string
28
- */
29
- export function formatDuration(seconds) {
30
- const hours = Math.floor(seconds / 3600);
31
- const minutes = Math.floor((seconds % 3600) / 60);
32
- const secs = seconds % 60;
33
- if (hours > 0) {
34
- return `${hours}h ${minutes}m ${secs}s`;
35
- }
36
- if (minutes > 0) {
37
- return `${minutes}m ${secs}s`;
38
- }
39
- return `${secs}s`;
40
- }
41
- /**
42
- * Debounce function
43
- */
44
- export function debounce(func, delay) {
45
- let timeoutId;
46
- return (...args) => {
47
- clearTimeout(timeoutId);
48
- timeoutId = setTimeout(() => func(...args), delay);
49
- };
50
- }
51
- /**
52
- * Truncate string
53
- */
54
- export function truncate(str, length) {
55
- if (str.length <= length)
56
- return str;
57
- return `${str.slice(0, length)}...`;
58
- }
59
- /**
60
- * Get initials from name
61
- */
62
- export function getInitials(name) {
63
- return name
64
- .split(' ')
65
- .map((n) => n[0])
66
- .join('')
67
- .toUpperCase()
68
- .slice(0, 2);
69
- }
70
- /**
71
- * Format number with commas
72
- */
73
- export function formatNumber(num) {
74
- return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
75
- }
76
- /**
77
- * Calculate percentage
78
- */
79
- export function calculatePercentage(value, total) {
80
- if (total === 0)
81
- return 0;
82
- return Math.round((value / total) * 100);
83
- }
84
- /**
85
- * Format date/time to Vietnam timezone (UTC+7)
86
- * @param date - ISO date string or Date object
87
- * @param format - Output format (default: 'DD/MM/YYYY HH:mm')
88
- * @returns Formatted date string in UTC+7
89
- */
90
- export function formatDateTimeVN(date, format = 'DD/MM/YYYY HH:mm') {
91
- if (!date)
92
- return '';
93
- try {
94
- const d = new Date(date);
95
- if (isNaN(d.getTime()))
96
- return 'Invalid Date';
97
- // Convert to Vietnam timezone (UTC+7)
98
- const vietnamTime = new Date(d.getTime() + (7 * 60 * 60 * 1000));
99
- const day = String(vietnamTime.getUTCDate()).padStart(2, '0');
100
- const month = String(vietnamTime.getUTCMonth() + 1).padStart(2, '0');
101
- const year = vietnamTime.getUTCFullYear();
102
- const hours = String(vietnamTime.getUTCHours()).padStart(2, '0');
103
- const minutes = String(vietnamTime.getUTCMinutes()).padStart(2, '0');
104
- return format
105
- .replace('DD', day)
106
- .replace('MM', month)
107
- .replace('YYYY', String(year))
108
- .replace('HH', hours)
109
- .replace('mm', minutes);
110
- }
111
- catch {
112
- return 'Invalid Date';
113
- }
114
- }
115
- /**
116
- * Format ISO datetime to time only in Vietnam timezone (UTC+7)
117
- * @param date - ISO date string or Date object
118
- * @returns Time string in HH:mm format (UTC+7)
119
- */
120
- export function formatTimeVN(date) {
121
- return formatDateTimeVN(date, 'HH:mm');
122
- }
123
- /**
124
- * Format date only in Vietnam timezone (UTC+7)
125
- * @param date - ISO date string or Date object
126
- * @returns Date string in DD/MM/YYYY format (UTC+7)
127
- */
128
- export function formatDateVN(date) {
129
- return formatDateTimeVN(date, 'DD/MM/YYYY');
130
- }
131
- /**
132
- * Extract date part from ISO string in Vietnam timezone (for date input)
133
- * @param date - ISO date string or Date object
134
- * @returns Date string in YYYY-MM-DD format (for HTML date input)
135
- */
136
- export function getDateInputValue(date) {
137
- if (!date)
138
- return '';
139
- try {
140
- const d = new Date(date);
141
- if (isNaN(d.getTime()))
142
- return '';
143
- // Convert to Vietnam timezone (UTC+7)
144
- const vietnamTime = new Date(d.getTime() + (7 * 60 * 60 * 1000));
145
- const year = vietnamTime.getUTCFullYear();
146
- const month = String(vietnamTime.getUTCMonth() + 1).padStart(2, '0');
147
- const day = String(vietnamTime.getUTCDate()).padStart(2, '0');
148
- return `${year}-${month}-${day}`;
149
- }
150
- catch {
151
- return '';
152
- }
153
- }
154
- /**
155
- * Extract time part from ISO string in Vietnam timezone (for time input)
156
- * @param date - ISO date string or Date object
157
- * @returns Time string in HH:mm format (for HTML time input)
158
- */
159
- export function getTimeInputValue(date) {
160
- return formatTimeVN(date);
161
- }
162
- /**
163
- * Remove undefined, null, and empty string values from an object.
164
- * Useful for cleaning API payloads before sending to backend.
165
- */
166
- export function cleanPayload(payload) {
167
- return Object.fromEntries(Object.entries(payload).filter(([, v]) => v !== undefined && v !== '' && v !== null));
168
- }
1
+ import{clsx as t}from"clsx";import{twMerge as e}from"tailwind-merge";export function cn(...r){return e(t(r))}export function formatDate(t,e="DD/MM/YYYY"){const r=new Date(t),n=String(r.getDate()).padStart(2,"0"),a=String(r.getMonth()+1).padStart(2,"0"),o=r.getFullYear(),i=String(r.getHours()).padStart(2,"0"),u=String(r.getMinutes()).padStart(2,"0");return e.replace("DD",n).replace("MM",a).replace("YYYY",String(o)).replace("HH",i).replace("mm",u)}export function formatDuration(t){const e=Math.floor(t/3600),r=Math.floor(t%3600/60),n=t%60;return e>0?`${e}h ${r}m ${n}s`:r>0?`${r}m ${n}s`:`${n}s`}export function debounce(t,e){let r;return(...n)=>{clearTimeout(r),r=setTimeout(()=>t(...n),e)}}export function truncate(t,e){return t.length<=e?t:`${t.slice(0,e)}...`}export function getInitials(t){return t.split(" ").map(t=>t[0]).join("").toUpperCase().slice(0,2)}export function formatNumber(t){return t.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")}export function calculatePercentage(t,e){return 0===e?0:Math.round(t/e*100)}export function formatDateTimeVN(t,e="DD/MM/YYYY HH:mm"){if(!t)return"";try{const r=new Date(t);if(isNaN(r.getTime()))return"Invalid Date";const n=new Date(r.getTime()+252e5),a=String(n.getUTCDate()).padStart(2,"0"),o=String(n.getUTCMonth()+1).padStart(2,"0"),i=n.getUTCFullYear(),u=String(n.getUTCHours()).padStart(2,"0"),c=String(n.getUTCMinutes()).padStart(2,"0");return e.replace("DD",a).replace("MM",o).replace("YYYY",String(i)).replace("HH",u).replace("mm",c)}catch{return"Invalid Date"}}export function formatTimeVN(t){return formatDateTimeVN(t,"HH:mm")}export function formatDateVN(t){return formatDateTimeVN(t,"DD/MM/YYYY")}export function getDateInputValue(t){if(!t)return"";try{const e=new Date(t);if(isNaN(e.getTime()))return"";const r=new Date(e.getTime()+252e5),n=r.getUTCFullYear(),a=String(r.getUTCMonth()+1).padStart(2,"0");return`${n}-${a}-${String(r.getUTCDate()).padStart(2,"0")}`}catch{return""}}export function getTimeInputValue(t){return formatTimeVN(t)}export function cleanPayload(t){return Object.fromEntries(Object.entries(t).filter(([,t])=>void 0!==t&&""!==t&&null!==t))}
@@ -1,111 +1 @@
1
- import { z } from 'zod';
2
- // Validation schema for Choose the Correct Answer form
3
- export const chooseCorrectAnswerFormSchema = z.object({
4
- question: z.string().optional(),
5
- optionType: z.enum(['text', 'image']).default('text'),
6
- options: z
7
- .array(z.string())
8
- .min(2, 'Phải có ít nhất 2 đáp án!'),
9
- correctAnswer: z.union([z.number(), z.array(z.number())]).default(-1),
10
- multipleAnswers: z.boolean().default(false),
11
- imageUrl: z.string().optional(),
12
- explanation: z.string().optional(),
13
- askerName: z.string().optional(),
14
- answererName: z.string().optional(),
15
- isExample: z.boolean().default(false),
16
- points: z
17
- .union([z.string(), z.number()])
18
- .transform((val, ctx) => {
19
- if (val === '' || val === undefined || val === null) {
20
- ctx.addIssue({
21
- code: z.ZodIssueCode.custom,
22
- message: 'Vui lòng nhập điểm số!',
23
- });
24
- return z.NEVER;
25
- }
26
- const num = typeof val === 'string' ? parseInt(val, 10) : val;
27
- if (isNaN(num)) {
28
- ctx.addIssue({
29
- code: z.ZodIssueCode.custom,
30
- message: 'Vui lòng nhập điểm số hợp lệ!',
31
- });
32
- return z.NEVER;
33
- }
34
- return num;
35
- })
36
- .pipe(z.number().min(1, 'Điểm số phải >= 1')),
37
- }).superRefine((data, ctx) => {
38
- const filledOptions = data.options.filter((opt) => opt.trim() !== '');
39
- if (filledOptions.length < 2) {
40
- ctx.addIssue({
41
- code: z.ZodIssueCode.custom,
42
- message: data.optionType === 'image'
43
- ? 'Phải upload ít nhất 2 hình ảnh đáp án!'
44
- : 'Phải có ít nhất 2 đáp án được điền!',
45
- path: ['options'],
46
- });
47
- }
48
- // Validate each option based on type
49
- data.options.forEach((opt, index) => {
50
- if (opt.trim() === '') {
51
- ctx.addIssue({
52
- code: z.ZodIssueCode.custom,
53
- message: data.optionType === 'image'
54
- ? 'Vui lòng upload hình ảnh!'
55
- : 'Vui lòng nhập đáp án!',
56
- path: ['options', index],
57
- });
58
- }
59
- });
60
- if (data.multipleAnswers) {
61
- const answers = Array.isArray(data.correctAnswer)
62
- ? data.correctAnswer
63
- : data.correctAnswer === -1
64
- ? []
65
- : [data.correctAnswer];
66
- if (answers.length === 0) {
67
- ctx.addIssue({
68
- code: z.ZodIssueCode.custom,
69
- message: 'Vui lòng chọn ít nhất 1 đáp án đúng!',
70
- path: ['correctAnswer'],
71
- });
72
- return;
73
- }
74
- const normalizedAnswers = [...new Set(answers)];
75
- if (normalizedAnswers.length !== answers.length) {
76
- ctx.addIssue({
77
- code: z.ZodIssueCode.custom,
78
- message: 'Đáp án đúng không được trùng lặp!',
79
- path: ['correctAnswer'],
80
- });
81
- }
82
- normalizedAnswers.forEach((answer) => {
83
- if (answer < 0 || answer >= data.options.length) {
84
- ctx.addIssue({
85
- code: z.ZodIssueCode.custom,
86
- message: 'Đáp án đúng không hợp lệ!',
87
- path: ['correctAnswer'],
88
- });
89
- }
90
- });
91
- return;
92
- }
93
- const singleAnswer = Array.isArray(data.correctAnswer)
94
- ? (data.correctAnswer[0] ?? -1)
95
- : data.correctAnswer;
96
- if (singleAnswer === -1) {
97
- ctx.addIssue({
98
- code: z.ZodIssueCode.custom,
99
- message: 'Vui lòng chọn đáp án đúng!',
100
- path: ['correctAnswer'],
101
- });
102
- return;
103
- }
104
- if (singleAnswer < 0 || singleAnswer >= data.options.length) {
105
- ctx.addIssue({
106
- code: z.ZodIssueCode.custom,
107
- message: 'Đáp án đúng không hợp lệ!',
108
- path: ['correctAnswer'],
109
- });
110
- }
111
- });
1
+ import{z as e}from"zod";export const chooseCorrectAnswerFormSchema=e.object({question:e.string().optional(),optionType:e.enum(["text","image"]).default("text"),options:e.array(e.string()).min(2,"Phải ít nhất 2 đáp án!"),correctAnswer:e.union([e.number(),e.array(e.number())]).default(-1),multipleAnswers:e.boolean().default(!1),imageUrl:e.string().optional(),explanation:e.string().optional(),askerName:e.string().optional(),answererName:e.string().optional(),isExample:e.boolean().default(!1),points:e.union([e.string(),e.number()]).transform((s,n)=>{if(""===s||null==s)return n.addIssue({code:e.ZodIssueCode.custom,message:"Vui lòng nhập điểm số!"}),e.NEVER;const o="string"==typeof s?parseInt(s,10):s;return isNaN(o)?(n.addIssue({code:e.ZodIssueCode.custom,message:"Vui lòng nhập điểm số hợp lệ!"}),e.NEVER):o}).pipe(e.number().min(1,"Điểm số phải >= 1"))}).superRefine((s,n)=>{if(s.options.filter(e=>""!==e.trim()).length<2&&n.addIssue({code:e.ZodIssueCode.custom,message:"image"===s.optionType?"Phải upload ít nhất 2 hình ảnh đáp án!":"Phải có ít nhất 2 đáp án được điền!",path:["options"]}),s.options.forEach((o,t)=>{""===o.trim()&&n.addIssue({code:e.ZodIssueCode.custom,message:"image"===s.optionType?"Vui lòng upload hình ảnh!":"Vui lòng nhập đáp án!",path:["options",t]})}),s.multipleAnswers){const o=Array.isArray(s.correctAnswer)?s.correctAnswer:-1===s.correctAnswer?[]:[s.correctAnswer];if(0===o.length)return void n.addIssue({code:e.ZodIssueCode.custom,message:"Vui lòng chọn ít nhất 1 đáp án đúng!",path:["correctAnswer"]});const t=[...new Set(o)];return t.length!==o.length&&n.addIssue({code:e.ZodIssueCode.custom,message:"Đáp án đúng không được trùng lặp!",path:["correctAnswer"]}),void t.forEach(o=>{(o<0||o>=s.options.length)&&n.addIssue({code:e.ZodIssueCode.custom,message:"Đáp án đúng không hợp lệ!",path:["correctAnswer"]})})}const o=Array.isArray(s.correctAnswer)?s.correctAnswer[0]??-1:s.correctAnswer;-1!==o?(o<0||o>=s.options.length)&&n.addIssue({code:e.ZodIssueCode.custom,message:"Đáp án đúng không hợp lệ!",path:["correctAnswer"]}):n.addIssue({code:e.ZodIssueCode.custom,message:"Vui lòng chọn đáp án đúng!",path:["correctAnswer"]})});
@@ -1,19 +1 @@
1
- import { z } from 'zod';
2
- // Label position validation schema
3
- const labelPositionSchema = z.object({
4
- id: z.string(),
5
- number: z.number().min(1, 'Số thứ tự phải >= 1'),
6
- correctAnswer: z.string().min(1, 'Vui lòng chọn đáp án đúng!'),
7
- isPreFilled: z.boolean().optional(),
8
- });
9
- // Main form validation schema for Label the Picture
10
- export const labelThePictureFormSchema = z.object({
11
- imageUrl: z.string().min(1, 'Vui lòng upload hình ảnh!'),
12
- wordBank: z
13
- .array(z.string().min(1, 'Vui lòng nhập nội dung từ!'))
14
- .min(1, 'Vui lòng thêm ít nhất một từ vào word bank!')
15
- .refine((words) => words.every((word) => word.trim() !== ''), { message: 'Tất cả các từ trong word bank phải có giá trị!' }),
16
- labels: z
17
- .array(labelPositionSchema)
18
- .min(1, 'Vui lòng thêm ít nhất một vị trí cần điền!'),
19
- });
1
+ import{z as n}from"zod";const t=n.object({id:n.string(),number:n.number().min(1,"Số thứ tự phải >= 1"),correctAnswer:n.string().min(1,"Vui lòng chọn đáp án đúng!"),isPreFilled:n.boolean().optional()});export const labelThePictureFormSchema=n.object({imageUrl:n.string().min(1,"Vui lòng upload hình ảnh!"),wordBank:n.array(n.string().min(1,"Vui lòng nhập nội dung từ!")).min(1,"Vui lòng thêm ít nhất một từ vào word bank!").refine(n=>n.every(n=>""!==n.trim()),{message:"Tất cả các từ trong word bank phải có giá trị!"}),labels:n.array(t).min(1,"Vui lòng thêm ít nhất một vị trí cần điền!")});
@@ -1,46 +1 @@
1
- import { z } from 'zod';
2
- /**
3
- * LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP validation schema.
4
- * Single question format:
5
- * - options: at least 2 options, each with image (required) and label (optional)
6
- * - audioUrl: audio file for the question
7
- * - correctAnswer: index of the correct option
8
- */
9
- export const listenAndChooseFromAnswerGroupOptionSchema = z.object({
10
- id: z.string(),
11
- imageUrl: z.string().min(1, 'Vui lòng upload ảnh cho đáp án!'),
12
- label: z.string().optional(),
13
- });
14
- export const listenAndChooseFromAnswerGroupFormSchema = z.object({
15
- options: z
16
- .array(listenAndChooseFromAnswerGroupOptionSchema)
17
- .min(2, 'Cần tối thiểu 2 đáp án trong nhóm!'),
18
- audioUrl: z.string().min(1, 'Vui lòng upload file audio!'),
19
- correctAnswer: z.string().min(1, 'Vui lòng chọn đáp án đúng!').refine((val) => val !== '-1', {
20
- message: 'Vui lòng chọn đáp án đúng!',
21
- }),
22
- points: z
23
- .union([z.string(), z.number()])
24
- .transform((val, ctx) => {
25
- if (val === '' || val === undefined || val === null) {
26
- ctx.addIssue({
27
- code: z.ZodIssueCode.custom,
28
- message: 'Vui lòng nhập điểm!',
29
- });
30
- return z.NEVER;
31
- }
32
- const num = typeof val === 'string' ? parseInt(val, 10) : val;
33
- if (isNaN(num)) {
34
- ctx.addIssue({
35
- code: z.ZodIssueCode.custom,
36
- message: 'Vui lòng nhập điểm hợp lệ!',
37
- });
38
- return z.NEVER;
39
- }
40
- return num;
41
- })
42
- .pipe(z.number().min(1, 'Điểm phải >= 1'))
43
- .optional(),
44
- explanation: z.string().optional(),
45
- isBackup: z.boolean().optional(),
46
- });
1
+ import{z as n}from"zod";export const listenAndChooseFromAnswerGroupOptionSchema=n.object({id:n.string(),imageUrl:n.string().min(1,"Vui lòng upload ảnh cho đáp án!"),label:n.string().optional()});export const listenAndChooseFromAnswerGroupFormSchema=n.object({options:n.array(listenAndChooseFromAnswerGroupOptionSchema).min(2,"Cần tối thiểu 2 đáp án trong nhóm!"),audioUrl:n.string().min(1,"Vui lòng upload file audio!"),correctAnswer:n.string().min(1,"Vui lòng chọn đáp án đúng!").refine(n=>"-1"!==n,{message:"Vui lòng chọn đáp án đúng!"}),points:n.union([n.string(),n.number()]).transform((o,i)=>{if(""===o||null==o)return i.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm!"}),n.NEVER;const e="string"==typeof o?parseInt(o,10):o;return isNaN(e)?(i.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm hợp lệ!"}),n.NEVER):e}).pipe(n.number().min(1,"Điểm phải >= 1")).optional(),explanation:n.string().optional(),isBackup:n.boolean().optional()});
@@ -1,21 +1 @@
1
- import { z } from 'zod';
2
- // Scene object node schema
3
- const sceneObjectNodeSchema = z.object({
4
- id: z.string(),
5
- imageUrl: z.string().min(1, 'Vui lòng upload hình ảnh đối tượng!'),
6
- label: z.string().min(1, 'Vui lòng nhập tên đối tượng!'),
7
- x: z.number().min(0).max(100),
8
- y: z.number().min(0).max(100),
9
- width: z.number().min(1, 'Chiều rộng phải lớn hơn 0!').max(100),
10
- height: z.number().min(1, 'Chiều cao phải lớn hơn 0!').max(100),
11
- });
12
- // Main form validation schema for Listen and Choose Objects in Scene
13
- export const listenAndChooseObjectsInSceneFormSchema = z.object({
14
- audioUrl: z.string().min(1, 'Vui lòng upload file audio!'),
15
- backgroundImageUrl: z.string().min(1, 'Vui lòng upload ảnh nền!'),
16
- objectNodes: z
17
- .array(sceneObjectNodeSchema)
18
- .min(2, 'Vui lòng thêm ít nhất 2 đối tượng!'),
19
- correctObjectId: z.string().min(1, 'Vui lòng chọn đáp án đúng!'),
20
- isBackup: z.boolean().optional(),
21
- });
1
+ import{z as n}from"zod";const i=n.object({id:n.string(),imageUrl:n.string().min(1,"Vui lòng upload hình ảnh đối tượng!"),label:n.string().min(1,"Vui lòng nhập tên đối tượng!"),x:n.number().min(0).max(100),y:n.number().min(0).max(100),width:n.number().min(1,"Chiều rộng phải lớn hơn 0!").max(100),height:n.number().min(1,"Chiều cao phải lớn hơn 0!").max(100)});export const listenAndChooseObjectsInSceneFormSchema=n.object({audioUrl:n.string().min(1,"Vui lòng upload file audio!"),backgroundImageUrl:n.string().min(1,"Vui lòng upload ảnh nền!"),objectNodes:n.array(i).min(2,"Vui lòng thêm ít nhất 2 đối tượng!"),correctObjectId:n.string().min(1,"Vui lòng chọn đáp án đúng!"),isBackup:n.boolean().optional()});
@@ -1,34 +1 @@
1
- import { z } from 'zod';
2
- // Drag object node schema (sidebar objects - no position)
3
- const dragObjectNodeSchema = z.object({
4
- id: z.string(),
5
- imageUrl: z.string().min(1, 'Vui lòng upload hình ảnh đối tượng!'),
6
- label: z.string().min(1, 'Vui lòng nhập tên đối tượng!'),
7
- });
8
- // Drop zone schema (positioned on scene)
9
- const dropZoneSchema = z.object({
10
- id: z.string(),
11
- label: z.string().min(1, 'Vui lòng nhập nhãn vùng thả!'),
12
- x: z.number().min(0).max(100),
13
- y: z.number().min(0).max(100),
14
- width: z.number().min(1, 'Chiều rộng phải lớn hơn 0!').max(100),
15
- height: z.number().min(1, 'Chiều cao phải lớn hơn 0!').max(100),
16
- });
17
- // Main form validation schema for Listen and Drag Objects into Scene
18
- export const listenAndDragObjectsIntoSceneFormSchema = z.object({
19
- audioUrl: z.string().min(1, 'Vui lòng upload file audio!'),
20
- backgroundImageUrl: z.string().min(1, 'Vui lòng upload ảnh nền!'),
21
- objectNodes: z
22
- .array(dragObjectNodeSchema)
23
- .min(2, 'Vui lòng thêm ít nhất 2 đối tượng!'),
24
- dropZones: z
25
- .array(dropZoneSchema)
26
- .min(1, 'Vui lòng thêm ít nhất 1 vùng thả!'),
27
- correctObjectId: z.string().min(1, 'Vui lòng chọn đối tượng đúng!').refine((val) => val !== '-1', {
28
- message: 'Vui lòng chọn đối tượng đúng!',
29
- }),
30
- correctDropZoneId: z.string().min(1, 'Vui lòng chọn vùng thả đúng!').refine((val) => val !== '-1', {
31
- message: 'Vui lòng chọn vùng thả đúng!',
32
- }),
33
- isBackup: z.boolean().optional(),
34
- });
1
+ import{z as n}from"zod";const i=n.object({id:n.string(),imageUrl:n.string().min(1,"Vui lòng upload hình ảnh đối tượng!"),label:n.string().min(1,"Vui lòng nhập tên đối tượng!")}),t=n.object({id:n.string(),label:n.string().min(1,"Vui lòng nhập nhãn vùng thả!"),x:n.number().min(0).max(100),y:n.number().min(0).max(100),width:n.number().min(1,"Chiều rộng phải lớn hơn 0!").max(100),height:n.number().min(1,"Chiều cao phải lớn hơn 0!").max(100)});export const listenAndDragObjectsIntoSceneFormSchema=n.object({audioUrl:n.string().min(1,"Vui lòng upload file audio!"),backgroundImageUrl:n.string().min(1,"Vui lòng upload ảnh nền!"),objectNodes:n.array(i).min(2,"Vui lòng thêm ít nhất 2 đối tượng!"),dropZones:n.array(t).min(1,"Vui lòng thêm ít nhất 1 vùng thả!"),correctObjectId:n.string().min(1,"Vui lòng chọn đối tượng đúng!").refine(n=>"-1"!==n,{message:"Vui lòng chọn đối tượng đúng!"}),correctDropZoneId:n.string().min(1,"Vui lòng chọn vùng thả đúng!").refine(n=>"-1"!==n,{message:"Vui lòng chọn vùng thả đúng!"}),isBackup:n.boolean().optional()});
@@ -1,40 +1 @@
1
- import { z } from 'zod';
2
- export const listenAndSpeakAnswerFormSchema = z.object({
3
- audioUrl: z.string().min(1, 'Vui lòng upload file audio!'),
4
- expectedAnswers: z
5
- .array(z.string().min(1, 'Vui lòng nhập đáp án!'))
6
- .min(1, 'Cần tối thiểu 1 đáp án!')
7
- .refine((options) => options.filter((opt) => opt.trim() !== '').length >= 1, { message: 'Cần tối thiểu 1 đáp án hợp lệ!' }),
8
- explanation: z.string().optional(),
9
- points: z
10
- .union([z.string(), z.number()])
11
- .transform((val, ctx) => {
12
- if (val === '' || val === undefined || val === null) {
13
- ctx.addIssue({
14
- code: z.ZodIssueCode.custom,
15
- message: 'Vui lòng nhập điểm!',
16
- });
17
- return z.NEVER;
18
- }
19
- const num = typeof val === 'string' ? parseInt(val, 10) : val;
20
- if (isNaN(num)) {
21
- ctx.addIssue({
22
- code: z.ZodIssueCode.custom,
23
- message: 'Vui lòng nhập điểm hợp lệ!',
24
- });
25
- return z.NEVER;
26
- }
27
- return num;
28
- })
29
- .pipe(z.number().min(1, 'Điểm số phải >= 1')),
30
- backgroundImageUrl: z.string().optional(),
31
- arrowNode: z
32
- .object({
33
- x: z.number(),
34
- y: z.number(),
35
- width: z.number(),
36
- height: z.number(),
37
- })
38
- .optional(),
39
- isBackup: z.boolean().optional(),
40
- });
1
+ import{z as n}from"zod";export const listenAndSpeakAnswerFormSchema=n.object({audioUrl:n.string().min(1,"Vui lòng upload file audio!"),expectedAnswers:n.array(n.string().min(1,"Vui lòng nhập đáp án!")).min(1,"Cần tối thiểu 1 đáp án!").refine(n=>n.filter(n=>""!==n.trim()).length>=1,{message:"Cần tối thiểu 1 đáp án hợp lệ!"}),explanation:n.string().optional(),points:n.union([n.string(),n.number()]).transform((e,i)=>{if(""===e||null==e)return i.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm!"}),n.NEVER;const o="string"==typeof e?parseInt(e,10):e;return isNaN(o)?(i.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm hợp lệ!"}),n.NEVER):o}).pipe(n.number().min(1,"Điểm số phải >= 1")),backgroundImageUrl:n.string().optional(),arrowNode:n.object({x:n.number(),y:n.number(),width:n.number(),height:n.number()}).optional(),isBackup:n.boolean().optional()});
@@ -1,45 +1 @@
1
- import { z } from 'zod';
2
- // Shared config for point validation
3
- const pointsValidation = z
4
- .union([z.string(), z.number()])
5
- .transform((val, ctx) => {
6
- if (val === '' || val === undefined || val === null) {
7
- ctx.addIssue({
8
- code: z.ZodIssueCode.custom,
9
- message: 'Vui lòng nhập điểm!',
10
- });
11
- return z.NEVER;
12
- }
13
- const num = typeof val === 'string' ? parseInt(val, 10) : val;
14
- if (isNaN(num)) {
15
- ctx.addIssue({
16
- code: z.ZodIssueCode.custom,
17
- message: 'Điểm không hợp lệ!',
18
- });
19
- return z.NEVER;
20
- }
21
- return num;
22
- })
23
- .pipe(z.number().min(1, 'Điểm tối thiểu là 1'));
24
- // Shared validation for the group (Only the first question handles this validation if group doesn't exist yet)
25
- export const compareImagesGroupSchema = z.object({
26
- candidateImageUrl: z.string().min(1, "Vui lòng tải lên Candidate's Copy Image!"),
27
- examinerImageUrl: z.string().min(1, "Vui lòng tải lên Examiner's Copy Image!"),
28
- });
29
- // Validation for the actual question form data
30
- export const listenAndSpeakCompareImagesSchema = z.object({
31
- // Group fields (only visible/required if it's the first question)
32
- candidateImageUrl: z.string().optional(),
33
- examinerImageUrl: z.string().optional(),
34
- // Individual fields
35
- audioUrl: z.string({
36
- message: "Vui lòng tải lên File Audio câu hỏi!",
37
- }).min(1, 'Vui lòng tải lên File Audio câu hỏi!'),
38
- expectedAnswers: z
39
- .array(z.string().min(1, 'Vui lòng điền đáp án!'))
40
- .min(1, 'Phải có ít nhất 1 đáp án mong muốn!')
41
- .refine((options) => options.filter((opt) => opt.trim() !== '').length >= 1, { message: 'Phải điền ít nhất 1 đáp án hợp lệ!' }),
42
- explanation: z.string().optional(),
43
- points: pointsValidation,
44
- isBackup: z.boolean().optional(),
45
- });
1
+ import{z as n}from"zod";const i=n.union([n.string(),n.number()]).transform((i,e)=>{if(""===i||null==i)return e.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm!"}),n.NEVER;const t="string"==typeof i?parseInt(i,10):i;return isNaN(t)?(e.addIssue({code:n.ZodIssueCode.custom,message:"Điểm không hợp lệ!"}),n.NEVER):t}).pipe(n.number().min(1,"Điểm tối thiểu là 1"));export const compareImagesGroupSchema=n.object({candidateImageUrl:n.string().min(1,"Vui lòng tải lên Candidate's Copy Image!"),examinerImageUrl:n.string().min(1,"Vui lòng tải lên Examiner's Copy Image!")});export const listenAndSpeakCompareImagesSchema=n.object({candidateImageUrl:n.string().optional(),examinerImageUrl:n.string().optional(),audioUrl:n.string({message:"Vui lòng tải lên File Audio câu hỏi!"}).min(1,"Vui lòng tải lên File Audio câu hỏi!"),expectedAnswers:n.array(n.string().min(1,"Vui lòng điền đáp án!")).min(1,"Phải có ít nhất 1 đáp án mong muốn!").refine(n=>n.filter(n=>""!==n.trim()).length>=1,{message:"Phải điền ít nhất 1 đáp án hợp lệ!"}),explanation:n.string().optional(),points:i,isBackup:n.boolean().optional()});
@@ -1,19 +1 @@
1
- import { z } from 'zod';
2
- export const groupImageItemSchema = z.object({
3
- id: z.string(),
4
- url: z.string().min(1, 'Vui lòng cung cấp link hình ảnh'),
5
- label: z.string().min(1, 'Vui lòng nhập nhãn cho hình ảnh'),
6
- });
7
- export const listenAndSpeakImageGroupFormSchema = z.object({
8
- audioUrl: z.string({
9
- message: 'Vui lòng tải lên file âm thanh',
10
- }).min(1, 'Vui lòng tải lên file âm thanh'),
11
- expectedAnswers: z.array(z.string().min(1, 'Câu trả lời không được để trống'))
12
- .min(1, 'Cần ít nhất một câu trả lời khả thi'),
13
- explanation: z.string().optional(),
14
- points: z.number().min(0.1, 'Điểm số phải lớn hơn 0'),
15
- activeImageId: z.string().min(1, 'Vui lòng chọn hình ảnh cần hỏi').optional(),
16
- groupImages: z.array(groupImageItemSchema).min(1, 'Vui lòng thêm ít nhất một hình ảnh').optional(),
17
- groupNumber: z.number().optional(),
18
- isBackup: z.boolean().optional(),
19
- });
1
+ import{z as n}from"zod";export const groupImageItemSchema=n.object({id:n.string(),url:n.string().min(1,"Vui lòng cung cấp link hình ảnh"),label:n.string().min(1,"Vui lòng nhập nhãn cho hình ảnh")});export const listenAndSpeakImageGroupFormSchema=n.object({audioUrl:n.string({message:"Vui lòng tải lên file âm thanh"}).min(1,"Vui lòng tải lên file âm thanh"),expectedAnswers:n.array(n.string().min(1,"Câu trả lời không được để trống")).min(1,"Cần ít nhất một câu trả lời khả thi"),explanation:n.string().optional(),points:n.number().min(.1,"Điểm số phải lớn hơn 0"),activeImageId:n.string().min(1,"Vui lòng chọn hình ảnh cần hỏi").optional(),groupImages:n.array(groupImageItemSchema).min(1,"Vui lòng thêm ít nhất một hình ảnh").optional(),groupNumber:n.number().optional(),isBackup:n.boolean().optional()});
@@ -1,39 +1 @@
1
- import { z } from 'zod';
2
- export const infoExchangeFieldSchema = z.object({
3
- key: z.string().min(1, 'Câu hỏi không được để trống'),
4
- answer: z.string().min(1, 'Đáp án không được để trống'),
5
- });
6
- export const listenAndSpeakInfoExchangeSchema = z.object({
7
- // Group data
8
- title: z.string().min(1, 'Vui lòng nhập tiêu đề (VD: Katy\'s house)'),
9
- imageUrl: z.string().optional(),
10
- fields: z.array(infoExchangeFieldSchema).optional(), // DEPRECATED - backward compat
11
- candidateFields: z
12
- .array(infoExchangeFieldSchema)
13
- .min(1, 'Vui lòng thêm ít nhất 1 dòng cho Candidate\'s Copy'),
14
- examinerFields: z
15
- .array(infoExchangeFieldSchema)
16
- .min(1, 'Vui lòng thêm ít nhất 1 dòng cho Examiner\'s Copy'),
17
- // Per-question data
18
- audioUrl: z.string().min(1, 'Vui lòng tải lên file audio câu hỏi'),
19
- expectedAnswers: z
20
- .array(z.string().min(1, 'Đáp án không được để trống'))
21
- .min(1, 'Vui lòng nhập ít nhất 1 đáp án'),
22
- // Common
23
- explanation: z.string().optional(),
24
- points: z
25
- .union([z.string(), z.number()])
26
- .transform((val, ctx) => {
27
- if (val === '' || val === undefined || val === null) {
28
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Vui lòng nhập điểm' });
29
- return z.NEVER;
30
- }
31
- const num = typeof val === 'string' ? parseFloat(val) : val;
32
- if (isNaN(num)) {
33
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Điểm phải là số hợp lệ' });
34
- return z.NEVER;
35
- }
36
- return num;
37
- })
38
- .pipe(z.number().min(0.5, 'Điểm phải >= 0.5')),
39
- });
1
+ import{z as n}from"zod";export const infoExchangeFieldSchema=n.object({key:n.string().min(1,"Câu hỏi không được để trống"),answer:n.string().min(1,"Đáp án không được để trống")});export const listenAndSpeakInfoExchangeSchema=n.object({title:n.string().min(1,"Vui lòng nhập tiêu đề (VD: Katy's house)"),imageUrl:n.string().optional(),fields:n.array(infoExchangeFieldSchema).optional(),candidateFields:n.array(infoExchangeFieldSchema).min(1,"Vui lòng thêm ít nhất 1 dòng cho Candidate's Copy"),examinerFields:n.array(infoExchangeFieldSchema).min(1,"Vui lòng thêm ít nhất 1 dòng cho Examiner's Copy"),audioUrl:n.string().min(1,"Vui lòng tải lên file audio câu hỏi"),expectedAnswers:n.array(n.string().min(1,"Đáp án không được để trống")).min(1,"Vui lòng nhập ít nhất 1 đáp án"),explanation:n.string().optional(),points:n.union([n.string(),n.number()]).transform((i,e)=>{if(""===i||null==i)return e.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm"}),n.NEVER;const t="string"==typeof i?parseFloat(i):i;return isNaN(t)?(e.addIssue({code:n.ZodIssueCode.custom,message:"Điểm phải là số hợp lệ"}),n.NEVER):t}).pipe(n.number().min(.5,"Điểm phải >= 0.5"))});
@@ -1,32 +1 @@
1
- import { z } from 'zod';
2
- export const listenAndSpeakOddOneOutSchema = z.object({
3
- audioUrl: z.string().min(1, 'Vui lòng tải lên file audio câu hỏi'),
4
- images: z
5
- .array(z.string().min(1))
6
- .length(4, 'Phải có đúng 4 hình ảnh')
7
- .refine((imgs) => imgs.filter((img) => img.trim() !== '').length === 4, { message: 'Vui lòng tải đủ 4 hình ảnh' }),
8
- oddOneOutIndex: z
9
- .number({ message: 'Vui lòng chọn hình khác loại' })
10
- .min(0, 'Vui lòng chọn hình khác loại')
11
- .max(3, 'Chỉ số hình ảnh không hợp lệ'),
12
- expectedAnswers: z
13
- .array(z.string().min(1, 'Đáp án không được để trống'))
14
- .min(1, 'Vui lòng nhập ít nhất 1 đáp án mong muốn'),
15
- explanation: z.string().optional(),
16
- points: z
17
- .union([z.string(), z.number()])
18
- .transform((val, ctx) => {
19
- if (val === '' || val === undefined || val === null) {
20
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Vui lòng nhập điểm' });
21
- return z.NEVER;
22
- }
23
- const num = typeof val === 'string' ? parseFloat(val) : val;
24
- if (isNaN(num)) {
25
- ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Điểm phải là số hợp lệ' });
26
- return z.NEVER;
27
- }
28
- return num;
29
- })
30
- .pipe(z.number().min(0.5, 'Điểm phải >= 0.5')),
31
- isBackup: z.boolean().optional(),
32
- });
1
+ import{z as n}from"zod";export const listenAndSpeakOddOneOutSchema=n.object({audioUrl:n.string().min(1,"Vui lòng tải lên file audio câu hỏi"),images:n.array(n.string().min(1)).length(4,"Phải có đúng 4 hình ảnh").refine(n=>4===n.filter(n=>""!==n.trim()).length,{message:"Vui lòng tải đủ 4 hình ảnh"}),oddOneOutIndex:n.number({message:"Vui lòng chọn hình khác loại"}).min(0,"Vui lòng chọn hình khác loại").max(3,"Chỉ số hình ảnh không hợp lệ"),expectedAnswers:n.array(n.string().min(1,"Đáp án không được để trống")).min(1,"Vui lòng nhập ít nhất 1 đáp án mong muốn"),explanation:n.string().optional(),points:n.union([n.string(),n.number()]).transform((i,e)=>{if(""===i||null==i)return e.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm"}),n.NEVER;const s="string"==typeof i?parseFloat(i):i;return isNaN(s)?(e.addIssue({code:n.ZodIssueCode.custom,message:"Điểm phải là số hợp lệ"}),n.NEVER):s}).pipe(n.number().min(.5,"Điểm phải >= 0.5")),isBackup:n.boolean().optional()});
@@ -1,19 +1 @@
1
- import { z } from 'zod';
2
- export const listenAndSpeakQuestionListFormSchema = z.object({
3
- audioUrl: z.string({
4
- message: 'Vui lòng tải lên file âm thanh',
5
- }).min(1, 'Vui lòng tải lên file âm thanh'),
6
- activeQuestionId: z.string({
7
- message: 'Vui lòng chọn câu hỏi',
8
- }).min(1, 'Vui lòng chọn câu hỏi'),
9
- expectedAnswers: z.array(z.string().min(1, 'Vui lòng nhập đáp án')).min(1, 'Cần ít nhất 1 đáp án'),
10
- points: z.number().min(1, 'Điểm phải lớn hơn 0').default(1),
11
- explanation: z.string().optional().default(''),
12
- isBackup: z.boolean().optional(),
13
- });
14
- export const listenAndSpeakQuestionGroupSchema = z.object({
15
- questions: z.array(z.object({
16
- id: z.string(),
17
- text: z.string().min(1, 'Vui lòng nhập nội dung câu hỏi'),
18
- })).min(1, 'Cần ít nhất một câu hỏi'),
19
- });
1
+ import{z as n}from"zod";export const listenAndSpeakQuestionListFormSchema=n.object({audioUrl:n.string({message:"Vui lòng tải lên file âm thanh"}).min(1,"Vui lòng tải lên file âm thanh"),activeQuestionId:n.string({message:"Vui lòng chọn câu hỏi"}).min(1,"Vui lòng chọn câu hỏi"),expectedAnswers:n.array(n.string().min(1,"Vui lòng nhập đáp án")).min(1,"Cần ít nhất 1 đáp án"),points:n.number().min(1,"Điểm phải lớn hơn 0").default(1),explanation:n.string().optional().default(""),isBackup:n.boolean().optional()});export const listenAndSpeakQuestionGroupSchema=n.object({questions:n.array(n.object({id:n.string(),text:n.string().min(1,"Vui lòng nhập nội dung câu hỏi")})).min(1,"Cần ít nhất một câu hỏi")});