@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,545 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
4
- import { toast } from 'sonner';
5
- import { ReactFlow, Background, useNodesState, useEdgesState, ReactFlowProvider, useReactFlow, } from '@xyflow/react';
6
- import '@xyflow/react/dist/style.css';
7
- import { Lightbulb } from 'lucide-react';
8
- import { ColorNode } from './ColorNode';
9
- import { ColorRegionNode } from './ColorRegionNode';
10
- import { usePresignedFileUrl } from '../../../shared/lib/hooks';
11
- const nodeTypes = {
12
- clientColorNode: (props) => _jsx(ColorNode, { ...props, variant: "client" }),
13
- clientColorRegionNode: (props) => _jsx(ColorRegionNode, { ...props }),
14
- };
15
- function ColorRegionClientContent({ questionData, explanation, isReviewMode = false, config }) {
16
- const [imageLoaded, setImageLoaded] = useState(false);
17
- const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 });
18
- // Get presigned URL for background image from S3
19
- const { previewUrl: backgroundImagePreviewUrl, isLoading: isLoadingImage } = usePresignedFileUrl(questionData.backgroundImageUrl);
20
- // Refs
21
- const imageRef = useRef(null);
22
- const flowRef = useRef(null);
23
- // React Flow State
24
- const [nodes, setNodes, reactFlowOnNodesChange] = useNodesState([]);
25
- const [, , onEdgesChange] = useEdgesState([]);
26
- // Track which color is assigned to which region
27
- const [regionAssignments, setRegionAssignments] = useState({});
28
- // Handle image load
29
- const handleImageLoad = () => {
30
- if (imageRef.current && flowRef.current) {
31
- const flowWidth = flowRef.current.clientWidth - 100; // 50px padding on each side
32
- const flowHeight = flowRef.current.clientHeight - 260; // top section and bottom margin
33
- const naturalWidth = imageRef.current.naturalWidth || imageRef.current.clientWidth;
34
- const naturalHeight = imageRef.current.naturalHeight || imageRef.current.clientHeight;
35
- const widthRatio = flowWidth / naturalWidth;
36
- const heightRatio = flowHeight / naturalHeight;
37
- const scale = Math.min(widthRatio, heightRatio, 1);
38
- setImageDimensions({
39
- width: naturalWidth * scale,
40
- height: naturalHeight * scale,
41
- });
42
- setImageLoaded(true);
43
- }
44
- };
45
- // Handle filling color into region
46
- const handleFillRegionColor = useCallback((regionId, color) => {
47
- setRegionAssignments((prev) => ({
48
- ...prev,
49
- [regionId]: color,
50
- }));
51
- }, []);
52
- // Handle removing color from region (for client mode delete)
53
- const handleRemoveColorFromRegion = useCallback((regionId) => {
54
- const removedColor = regionAssignments[regionId];
55
- if (removedColor) {
56
- // Remove assignment
57
- setRegionAssignments((prev) => {
58
- const newAssignments = { ...prev };
59
- delete newAssignments[regionId];
60
- return newAssignments;
61
- });
62
- // Restore color node visibility
63
- setNodes((prevNodes) => prevNodes.map((n) => n.data?.color === removedColor ? { ...n, data: { ...n.data, isConnected: false } } : n));
64
- }
65
- }, [regionAssignments, setNodes]);
66
- // State for visual feedback
67
- const [highlightedRegionId, setHighlightedRegionId] = useState(null);
68
- const { getIntersectingNodes } = useReactFlow();
69
- const imageStartY = 200;
70
- const imageStartX = (() => {
71
- if (!imageLoaded || imageDimensions.width <= 0)
72
- return 50;
73
- const flowWidth = flowRef.current?.clientWidth || 0;
74
- if (!flowWidth)
75
- return 50;
76
- const centered = (flowWidth - imageDimensions.width) / 2;
77
- return Math.max(50, centered);
78
- })();
79
- // Track if a node is being dragged to prevent panning
80
- const isDraggingNodeRef = useRef(false);
81
- // Track the old region ID when dragging starts (for swap functionality)
82
- const draggedNodeOldRegionRef = useRef(null);
83
- // Handle node drag start - store old region for potential swap
84
- const onNodeDragStart = useCallback((_, node) => {
85
- // Only handle color nodes
86
- if (node.type !== 'clientColorNode') {
87
- return;
88
- }
89
- // Set flag to prevent panning
90
- isDraggingNodeRef.current = true;
91
- const draggedColor = node.data?.color;
92
- if (!draggedColor || typeof draggedColor !== 'string') {
93
- return;
94
- }
95
- // Check if this color node is from a region (has regionId in data)
96
- const regionId = node.data?.regionId;
97
- // Find the old region this color was assigned to (either from regionId or from assignments)
98
- const assignedRegionId = regionId ||
99
- Object.keys(regionAssignments).find((id) => regionAssignments[id] === draggedColor);
100
- // Store the old region ID for potential swap
101
- draggedNodeOldRegionRef.current = assignedRegionId || null;
102
- if (assignedRegionId) {
103
- // Temporarily remove the old assignment (will be restored or swapped)
104
- setRegionAssignments((prev) => {
105
- const newAssignments = { ...prev };
106
- delete newAssignments[assignedRegionId];
107
- return newAssignments;
108
- });
109
- // Ngay lập tức cập nhật lại region node để hiển thị vùng drop
110
- setNodes((prevNodes) => prevNodes.map((n) => {
111
- if (n.id !== assignedRegionId || n.type !== 'clientColorRegionNode') {
112
- return n;
113
- }
114
- return {
115
- ...n,
116
- data: {
117
- ...n.data,
118
- savedColor: '',
119
- },
120
- style: {
121
- ...n.style,
122
- pointerEvents: 'auto',
123
- },
124
- };
125
- }));
126
- }
127
- }, [regionAssignments, setNodes]);
128
- const onNodeDrag = useCallback((_, node) => {
129
- if (node.type !== 'clientColorNode') {
130
- return;
131
- }
132
- const draggedColor = node.data?.color;
133
- if (!draggedColor || typeof draggedColor !== 'string') {
134
- setHighlightedRegionId(null);
135
- return;
136
- }
137
- const parseDimension = (value) => {
138
- if (typeof value === 'number') {
139
- return Number.isFinite(value) ? value : null;
140
- }
141
- if (typeof value === 'string') {
142
- const parsed = parseFloat(value);
143
- return Number.isFinite(parsed) ? parsed : null;
144
- }
145
- return null;
146
- };
147
- const getNodeDimension = (targetNode, key) => {
148
- const direct = key in targetNode ? targetNode[key] : null;
149
- if (typeof direct === 'number' && Number.isFinite(direct)) {
150
- return direct;
151
- }
152
- const styleValue = targetNode.measured
153
- ? targetNode.measured[key]
154
- : undefined;
155
- return parseDimension(styleValue);
156
- };
157
- const getRegionPixelSize = (targetRegion) => {
158
- if (imageDimensions.width <= 0 || imageDimensions.height <= 0) {
159
- return null;
160
- }
161
- const styleWidth = parseDimension(targetRegion.style?.width);
162
- const styleHeight = parseDimension(targetRegion.style?.height);
163
- if (styleWidth && styleHeight) {
164
- return {
165
- width: Math.max(styleWidth, 60),
166
- height: Math.max(styleHeight, 40),
167
- };
168
- }
169
- const regionData = targetRegion.data;
170
- if (regionData?.width === undefined ||
171
- regionData?.height === undefined ||
172
- typeof regionData.width !== 'number' ||
173
- typeof regionData.height !== 'number') {
174
- return null;
175
- }
176
- return {
177
- width: Math.max((regionData.width / 100) * imageDimensions.width, 60),
178
- height: Math.max((regionData.height / 100) * imageDimensions.height, 40),
179
- };
180
- };
181
- const highlightedColorNodeClass = '';
182
- const draggingColorNodeClass = ''; // Bỏ border khi đang drag
183
- const intersectingNodes = getIntersectingNodes(node, true);
184
- const colorNodeIds = intersectingNodes
185
- .filter((n) => n.type === 'clientColorNode')
186
- .map((n) => n.id);
187
- const intersectingRegions = intersectingNodes.filter((n) => n.type === 'clientColorRegionNode' && n.id !== 'unassigned-region');
188
- const nodeY = node.position.y;
189
- const isInUnassignedRegion = nodeY >= 0 && nodeY <= 100;
190
- if (intersectingRegions.length > 0) {
191
- setHighlightedRegionId(intersectingRegions[0].id);
192
- if (!colorNodeIds.includes(node.id)) {
193
- colorNodeIds.push(node.id);
194
- }
195
- }
196
- else if (isInUnassignedRegion) {
197
- setHighlightedRegionId('unassigned-region');
198
- }
199
- else {
200
- setHighlightedRegionId(null);
201
- }
202
- setNodes((prevNodes) => prevNodes.map((n) => n.type === 'clientColorNode'
203
- ? {
204
- ...n,
205
- data: {
206
- ...n.data,
207
- highlightClass: colorNodeIds.includes(n.id)
208
- ? highlightedColorNodeClass
209
- : n.id === node.id
210
- ? draggingColorNodeClass
211
- : undefined,
212
- highlightStyle: colorNodeIds.includes(n.id) &&
213
- n.id === node.id &&
214
- intersectingRegions.length > 0
215
- ? (() => {
216
- const targetRegion = intersectingRegions[0];
217
- const regionSize = getRegionPixelSize(targetRegion);
218
- const nodeWidth = getNodeDimension(n, 'width');
219
- const nodeHeight = getNodeDimension(n, 'height');
220
- if (!regionSize || !nodeWidth || !nodeHeight) {
221
- return undefined;
222
- }
223
- return {
224
- transform: `scale(${Number(regionSize.width) / Number(nodeWidth)}, ${Number(regionSize.height) / Number(nodeHeight)})`,
225
- transformOrigin: 'center center',
226
- transition: 'transform 180ms ease-out',
227
- };
228
- })()
229
- : undefined,
230
- isHighlighted: colorNodeIds.includes(n.id),
231
- },
232
- }
233
- : n));
234
- }, [getIntersectingNodes, imageDimensions, setNodes]);
235
- // Handle node drag stop (drop) with intersection detection
236
- const onNodeDragStop = useCallback((_, node) => {
237
- if (node.type !== 'clientColorNode')
238
- return;
239
- setNodes((prevNodes) => prevNodes.map((n) => n.type === 'clientColorNode'
240
- ? {
241
- ...n,
242
- data: {
243
- ...n.data,
244
- highlightClass: undefined,
245
- highlightStyle: undefined,
246
- isHighlighted: false,
247
- },
248
- }
249
- : n));
250
- const draggedColor = node.data?.color;
251
- if (!draggedColor || typeof draggedColor !== 'string') {
252
- setHighlightedRegionId(null);
253
- return;
254
- }
255
- // Get intersecting nodes using React Flow's built-in method with partial intersection
256
- const intersectingNodes = getIntersectingNodes(node, true); // partially = true
257
- // Find intersecting region nodes (excluding unassigned region - we'll check it manually)
258
- const intersectingRegions = intersectingNodes.filter((n) => n.type === 'clientColorRegionNode' && n.id !== 'unassigned-region');
259
- // Manually check if node is in unassigned region area (top 100px)
260
- const nodeY = node.position.y;
261
- const isInUnassignedRegion = nodeY >= 0 && nodeY <= 100;
262
- // Clear highlight
263
- setHighlightedRegionId(null);
264
- if (intersectingRegions.length > 0) {
265
- const targetRegion = intersectingRegions[0];
266
- const oldRegionId = draggedNodeOldRegionRef.current;
267
- // Drop into a regular region
268
- const existingColorInTargetRegion = regionAssignments[targetRegion.id];
269
- if (existingColorInTargetRegion && existingColorInTargetRegion !== draggedColor) {
270
- // Swap: Target region already has a color node
271
- // 1. Assign dragged color to target region
272
- // 2. Assign existing color to old region (if exists) or remove it
273
- setRegionAssignments((prev) => {
274
- const newAssignments = { ...prev };
275
- // Assign dragged color to target region
276
- newAssignments[targetRegion.id] = draggedColor;
277
- // If dragged color had an old region, assign existing color to old region
278
- if (oldRegionId) {
279
- newAssignments[oldRegionId] = existingColorInTargetRegion;
280
- }
281
- // If dragged color had no old region, existing color will be removed from assignments
282
- // (it will go back to original position - already removed when we set newAssignments)
283
- return newAssignments;
284
- });
285
- }
286
- else {
287
- // Simple assignment: Target region is empty or same color
288
- setRegionAssignments((prev) => ({
289
- ...prev,
290
- [targetRegion.id]: draggedColor,
291
- }));
292
- }
293
- // Clear the ref
294
- draggedNodeOldRegionRef.current = null;
295
- }
296
- else if (isInUnassignedRegion) {
297
- // Drop into unassigned region area - remove assignment and return to original position
298
- const oldRegionId = draggedNodeOldRegionRef.current;
299
- if (oldRegionId) {
300
- setRegionAssignments((prev) => {
301
- const newAssignments = { ...prev };
302
- delete newAssignments[oldRegionId];
303
- return newAssignments;
304
- });
305
- }
306
- // Reset node position to original position
307
- const originalNode = questionData.colorNodes.find((n) => n.color === draggedColor);
308
- if (originalNode) {
309
- const currentAssignments = oldRegionId
310
- ? Object.fromEntries(Object.entries(regionAssignments).filter(([id]) => id !== oldRegionId))
311
- : regionAssignments;
312
- const unassignedColors = questionData.colorNodes.filter((n) => !Object.values(currentAssignments).includes(n.color) || n.color === draggedColor);
313
- const itemsPerRow = 4;
314
- const nodeIndex = unassignedColors.findIndex((n) => n.id === originalNode.id);
315
- const row = Math.floor(nodeIndex / itemsPerRow);
316
- const col = nodeIndex % itemsPerRow;
317
- const originalPosition = { x: 50 + col * 200, y: 20 + row * 80 };
318
- // Update node position
319
- setNodes((prevNodes) => prevNodes.map((n) => (n.id === node.id ? { ...n, position: originalPosition } : n)));
320
- }
321
- draggedNodeOldRegionRef.current = null;
322
- }
323
- else {
324
- // Drop outside any valid region - return to original position
325
- // If it was assigned to a region, remove the assignment
326
- if (draggedNodeOldRegionRef.current) {
327
- setRegionAssignments((prev) => {
328
- const newAssignments = { ...prev };
329
- delete newAssignments[draggedNodeOldRegionRef.current];
330
- return newAssignments;
331
- });
332
- }
333
- // Reset node position to original position
334
- const originalNode = questionData.colorNodes.find((n) => n.color === draggedColor);
335
- if (originalNode) {
336
- const currentAssignments = draggedNodeOldRegionRef.current
337
- ? Object.fromEntries(Object.entries(regionAssignments).filter(([id]) => id !== draggedNodeOldRegionRef.current))
338
- : regionAssignments;
339
- const unassignedColors = questionData.colorNodes.filter((n) => !Object.values(currentAssignments).includes(n.color) || n.color === draggedColor);
340
- const itemsPerRow = 4;
341
- const nodeIndex = unassignedColors.findIndex((n) => n.id === originalNode.id);
342
- const row = Math.floor(nodeIndex / itemsPerRow);
343
- const col = nodeIndex % itemsPerRow;
344
- const originalPosition = { x: 50 + col * 200, y: 20 + row * 80 };
345
- // Update node position
346
- setNodes((prevNodes) => prevNodes.map((n) => (n.id === node.id ? { ...n, position: originalPosition } : n)));
347
- }
348
- // Clear the ref
349
- draggedNodeOldRegionRef.current = null;
350
- }
351
- // Clear dragging flag
352
- isDraggingNodeRef.current = false;
353
- }, [getIntersectingNodes, regionAssignments, questionData.colorNodes, setNodes]);
354
- // Memoize color nodes to avoid unnecessary recalculations
355
- // Only show color nodes that are NOT assigned to any region (they will be shown in regions when assigned)
356
- const colorFlowNodes = useMemo(() => {
357
- if (!questionData.colorNodes || questionData.colorNodes.length === 0) {
358
- return [];
359
- }
360
- // Filter out color nodes that are already assigned to regions
361
- const unassignedColors = questionData.colorNodes.filter((node) => !Object.values(regionAssignments).includes(node.color));
362
- const itemsPerRow = 4; // 4 items per row
363
- return unassignedColors.map((node, index) => {
364
- const row = Math.floor(index / itemsPerRow);
365
- const col = index % itemsPerRow;
366
- return {
367
- id: node.id,
368
- type: 'clientColorNode',
369
- position: { x: 50 + col * 200, y: 20 + row * 80 },
370
- data: {
371
- color: node.color,
372
- isEditMode: false,
373
- isConnected: false, // These are unassigned, so always false
374
- displayMode: 'compact',
375
- onRemove: () => {
376
- // Remove assignment when color node is removed
377
- const regionId = Object.keys(regionAssignments).find((id) => regionAssignments[id] === node.color);
378
- if (regionId) {
379
- setRegionAssignments((prev) => {
380
- const newAssignments = { ...prev };
381
- delete newAssignments[regionId];
382
- return newAssignments;
383
- });
384
- }
385
- },
386
- },
387
- draggable: true, // Enable dragging in client mode
388
- };
389
- });
390
- }, [questionData.colorNodes, regionAssignments]);
391
- // Memoize color nodes that are assigned to regions (render them inside regions for dragging)
392
- const colorNodesInRegions = useMemo(() => {
393
- if (!imageLoaded || imageDimensions.width <= 0)
394
- return [];
395
- return questionData.regionNodes
396
- .filter((region) => regionAssignments[region.id]) // Only regions with assigned colors
397
- .map((region) => {
398
- const assignedColor = regionAssignments[region.id];
399
- const colorNodeData = questionData.colorNodes.find((n) => n.color === assignedColor);
400
- if (!colorNodeData)
401
- return null;
402
- // Position and size color node relative to region
403
- const flowWidth = flowRef.current?.clientWidth || 0;
404
- const startX = flowWidth > 0 && imageDimensions.width > 0
405
- ? Math.max(50, (flowWidth - imageDimensions.width) / 2)
406
- : 50;
407
- const regionX = startX + (region.x / 100) * imageDimensions.width;
408
- const regionY = imageStartY + (region.y / 100) * imageDimensions.height;
409
- const regionWidth = (region.width / 100) * imageDimensions.width;
410
- const regionHeight = (region.height / 100) * imageDimensions.height;
411
- // Apply min dimensions to match ColorRegionNode behavior
412
- const minWidth = 60;
413
- const minHeight = 40;
414
- const finalWidth = Math.max(regionWidth, minWidth);
415
- const finalHeight = Math.max(regionHeight, minHeight);
416
- return {
417
- id: `color-in-region-${region.id}`,
418
- type: 'clientColorNode',
419
- position: {
420
- x: regionX,
421
- y: regionY,
422
- },
423
- data: {
424
- color: assignedColor,
425
- isEditMode: false,
426
- isConnected: true,
427
- regionId: region.id, // Store region ID for reference
428
- onRemove: () => handleRemoveColorFromRegion(region.id),
429
- },
430
- draggable: true,
431
- width: finalWidth,
432
- height: finalHeight,
433
- style: {
434
- width: `${finalWidth}px`,
435
- height: `${finalHeight}px`,
436
- margin: 0,
437
- padding: 0,
438
- boxSizing: 'border-box',
439
- },
440
- };
441
- })
442
- .filter((node) => node !== null);
443
- }, [
444
- imageLoaded,
445
- imageDimensions,
446
- questionData.regionNodes,
447
- questionData.colorNodes,
448
- regionAssignments,
449
- handleRemoveColorFromRegion,
450
- ]);
451
- // Memoize region nodes to avoid unnecessary recalculations
452
- // Regions will be positioned on the image in the middle section
453
- const regionFlowNodes = useMemo(() => {
454
- if (!imageLoaded || imageDimensions.width <= 0)
455
- return [];
456
- // Calculate image position in the middle section
457
- // Image starts after color nodes section (top ~200px) and is centered
458
- const imageStartY = 200; // Start image after color nodes
459
- const imageStartX = 50; // Left margin
460
- const flowWidth = flowRef.current?.clientWidth || 0;
461
- const imageLeft = flowWidth > 0 ? (flowWidth - imageDimensions.width) / 2 : imageStartX;
462
- return questionData.regionNodes.map((region) => {
463
- const savedColor = regionAssignments[region.id] || '';
464
- return {
465
- id: region.id,
466
- type: 'clientColorRegionNode',
467
- position: {
468
- x: imageLeft + (region.x / 100) * imageDimensions.width,
469
- y: imageStartY + (region.y / 100) * imageDimensions.height,
470
- },
471
- data: {
472
- color: region.color,
473
- x: region.x,
474
- y: region.y,
475
- width: region.width,
476
- height: region.height,
477
- isEditMode: false,
478
- isClientMode: true,
479
- savedColor: savedColor, // Only use assignments from client interactions
480
- onFillColor: (color) => handleFillRegionColor(region.id, color),
481
- onRemove: () => handleRemoveColorFromRegion(region.id), // Add remove handler for client mode
482
- isHighlighted: highlightedRegionId === region.id, // Pass highlight state
483
- },
484
- draggable: false,
485
- style: {
486
- width: `${(region.width / 100) * imageDimensions.width}px`,
487
- height: `${(region.height / 100) * imageDimensions.height}px`,
488
- pointerEvents: (savedColor ? 'none' : 'auto'), // Disable pointer events when ColorNode is assigned
489
- },
490
- zIndex: 0, // Ensure regions are below color nodes
491
- };
492
- });
493
- }, [
494
- imageLoaded,
495
- imageDimensions,
496
- questionData.regionNodes,
497
- regionAssignments,
498
- handleFillRegionColor,
499
- handleRemoveColorFromRegion,
500
- highlightedRegionId,
501
- ]);
502
- // Update nodes when dependencies change
503
- useEffect(() => {
504
- // Skip updating nodes if we're currently dragging a node
505
- // This prevents position reset during drag operations
506
- if (isDraggingNodeRef.current) {
507
- return;
508
- }
509
- // Don't include unassigned region as a React Flow node - render it as background instead
510
- const allNodes = [...regionFlowNodes, ...colorFlowNodes, ...colorNodesInRegions];
511
- setNodes(allNodes);
512
- }, [
513
- colorFlowNodes,
514
- colorNodesInRegions,
515
- regionFlowNodes,
516
- setNodes,
517
- imageLoaded,
518
- imageDimensions,
519
- ]);
520
- return (_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900", children: config.clientTitle }), _jsx("p", { className: "text-sm text-gray-500", children: config.clientSubtitle })] }) }), questionData.instructions && questionData.instructions.length > 0 && (_jsxs("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-gray-100 bg-gradient-to-r from-blue-50 to-indigo-50 px-4 py-2.5", children: [_jsx("svg", { className: "h-5 w-5 text-blue-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" }) }), _jsx("h3", { className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn" })] }), _jsx("div", { className: "divide-y divide-gray-50 mt-2", children: questionData.instructions.map((instruction, index) => (_jsx("div", { className: "flex items-center gap-3 px-5 py-2 transition-colors hover:bg-gray-50", children: _jsxs("span", { className: "text-sm font-semibold text-gray-700", children: [index + 1, ". ", instruction] }) }, index))) })] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-lg", children: [_jsx("div", { className: "border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-red-400" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-yellow-400" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-green-400" }), _jsx("span", { className: "ml-3 text-sm font-medium text-gray-500", children: "Workspace" })] }) }), _jsxs("div", { className: "relative bg-gradient-to-br from-slate-50 to-gray-100", style: { height: '800px', width: '100%' }, ref: flowRef, children: [_jsx("div", { className: "pointer-events-none absolute", style: {
521
- left: `${imageStartX}px`,
522
- top: `${imageStartY}px`,
523
- width: imageDimensions.width ? `${imageDimensions.width}px` : 'auto',
524
- height: imageDimensions.height ? `${imageDimensions.height}px` : 'auto',
525
- zIndex: 0,
526
- }, children: isLoadingImage ? (_jsx("div", { className: "flex h-full w-full items-center justify-center text-gray-500", children: "\u0110ang t\u1EA3i h\u00ECnh \u1EA3nh..." })) : backgroundImagePreviewUrl ? (_jsx("img", { ref: imageRef, src: backgroundImagePreviewUrl, alt: "Background", className: "h-full w-full object-contain", style: imageDimensions.width && imageDimensions.height
527
- ? {
528
- width: `${imageDimensions.width}px`,
529
- height: `${imageDimensions.height}px`,
530
- }
531
- : undefined, onLoad: handleImageLoad, onError: () => {
532
- toast.error('Không thể load ảnh!');
533
- setImageLoaded(false);
534
- } })) : (_jsx("div", { className: "flex h-full w-full items-center justify-center text-gray-500", children: "Kh\u00F4ng c\u00F3 h\u00ECnh \u1EA3nh" })) }), _jsx(ReactFlow, { nodes: nodes, edges: [], onNodesChange: reactFlowOnNodesChange, onNodeDragStart: onNodeDragStart, onNodeDrag: onNodeDrag, onNodeDragStop: onNodeDragStop, onEdgesChange: onEdgesChange, nodeTypes: nodeTypes, defaultViewport: { x: 0, y: 0, zoom: 1 }, fitView: false, nodesDraggable: true, nodesConnectable: false, elementsSelectable: false, panOnDrag: false, panOnScroll: false, zoomOnScroll: false, zoomOnDoubleClick: false, zoomOnPinch: false, preventScrolling: true, autoPanOnNodeDrag: false, autoPanOnConnect: false, proOptions: { hideAttribution: true }, style: {
535
- width: '100%',
536
- height: '100%',
537
- position: 'absolute',
538
- top: 0,
539
- left: 0,
540
- zIndex: 10,
541
- }, children: _jsx(Background, {}) })] })] }), isReviewMode && explanation && (_jsx("div", { className: "rounded-xl bg-gradient-to-r from-blue-50 to-indigo-50 p-4 border border-blue-200", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-white", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("span", { className: "text-xs font-semibold text-blue-700 uppercase tracking-wider", children: "Gi\u1EA3i th\u00EDch" }), _jsx("p", { className: "mt-1 text-sm text-gray-700 leading-relaxed", children: explanation })] })] }) }))] }));
542
- }
543
- export function ColorRegionClient(props) {
544
- return (_jsx(ReactFlowProvider, { children: _jsx(ColorRegionClientContent, { ...props }) }));
545
- }
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as i,useRef as r,useEffect as o,useCallback as n,useMemo as l}from"react";import{toast as d}from"sonner";import{ReactFlow as s,Background as a,useNodesState as c,useEdgesState as h,ReactFlowProvider as u,useReactFlow as g}from"@xyflow/react";import"@xyflow/react/dist/style.css";import{Lightbulb as m}from"lucide-react";import{ColorNode as f}from"./ColorNode";import{ColorRegionNode as p}from"./ColorRegionNode";import{usePresignedFileUrl as x}from"../../../shared/lib/hooks";const y={clientColorNode:t=>e(f,{...t,variant:"client"}),clientColorRegionNode:t=>e(p,{...t})};function N({questionData:u,explanation:f,isReviewMode:p=!1,config:N}){const[b,w]=i(!1),[v,C]=i({width:0,height:0}),{previewUrl:M,isLoading:j}=x(u.backgroundImageUrl),O=r(null),k=r(null),[R,S,$]=c([]),[,,E]=h([]),[I,z]=i({}),D=n((e,t)=>{z(i=>({...i,[e]:t}))},[]),H=n(e=>{const t=I[e];t&&(z(t=>{const i={...t};return delete i[e],i}),S(e=>e.map(e=>e.data?.color===t?{...e,data:{...e.data,isConnected:!1}}:e)))},[I,S]),[W,F]=i(null),{getIntersectingNodes:L}=g(),P=(()=>{if(!b||v.width<=0)return 50;const e=k.current?.clientWidth||0;if(!e)return 50;const t=(e-v.width)/2;return Math.max(50,t)})(),V=r(!1),B=r(null),K=n((e,t)=>{if("clientColorNode"!==t.type)return;V.current=!0;const i=t.data?.color;if(!i||"string"!=typeof i)return;const r=t.data?.regionId,o=r||Object.keys(I).find(e=>I[e]===i);B.current=o||null,o&&(z(e=>{const t={...e};return delete t[o],t}),S(e=>e.map(e=>e.id!==o||"clientColorRegionNode"!==e.type?e:{...e,data:{...e.data,savedColor:""},style:{...e.style,pointerEvents:"auto"}})))},[I,S]),T=n((e,t)=>{if("clientColorNode"!==t.type)return;const i=t.data?.color;if(!i||"string"!=typeof i)return void F(null);const r=e=>{if("number"==typeof e)return Number.isFinite(e)?e:null;if("string"==typeof e){const t=parseFloat(e);return Number.isFinite(t)?t:null}return null},o=(e,t)=>{const i=t in e?e[t]:null;if("number"==typeof i&&Number.isFinite(i))return i;const o=e.measured?e.measured[t]:void 0;return r(o)},n=L(t,!0),l=n.filter(e=>"clientColorNode"===e.type).map(e=>e.id),d=n.filter(e=>"clientColorRegionNode"===e.type&&"unassigned-region"!==e.id),s=t.position.y,a=s>=0&&s<=100;d.length>0?(F(d[0].id),l.includes(t.id)||l.push(t.id)):F(a?"unassigned-region":null),S(e=>e.map(e=>"clientColorNode"===e.type?{...e,data:{...e.data,highlightClass:l.includes(e.id)||e.id===t.id?"":void 0,highlightStyle:l.includes(e.id)&&e.id===t.id&&d.length>0?(()=>{const t=(e=>{if(v.width<=0||v.height<=0)return null;const t=r(e.style?.width),i=r(e.style?.height);if(t&&i)return{width:Math.max(t,60),height:Math.max(i,40)};const o=e.data;return void 0===o?.width||void 0===o?.height||"number"!=typeof o.width||"number"!=typeof o.height?null:{width:Math.max(o.width/100*v.width,60),height:Math.max(o.height/100*v.height,40)}})(d[0]),i=o(e,"width"),n=o(e,"height");if(t&&i&&n)return{transform:`scale(${Number(t.width)/Number(i)}, ${Number(t.height)/Number(n)})`,transformOrigin:"center center",transition:"transform 180ms ease-out"}})():void 0,isHighlighted:l.includes(e.id)}}:e))},[L,v,S]),U=n((e,t)=>{if("clientColorNode"!==t.type)return;S(e=>e.map(e=>"clientColorNode"===e.type?{...e,data:{...e.data,highlightClass:void 0,highlightStyle:void 0,isHighlighted:!1}}:e));const i=t.data?.color;if(!i||"string"!=typeof i)return void F(null);const r=L(t,!0).filter(e=>"clientColorRegionNode"===e.type&&"unassigned-region"!==e.id),o=t.position.y,n=o>=0&&o<=100;if(F(null),r.length>0){const e=r[0],t=B.current,o=I[e.id];z(o&&o!==i?r=>{const n={...r};return n[e.id]=i,t&&(n[t]=o),n}:t=>({...t,[e.id]:i})),B.current=null}else if(n){const e=B.current;e&&z(t=>{const i={...t};return delete i[e],i});const r=u.colorNodes.find(e=>e.color===i);if(r){const o=e?Object.fromEntries(Object.entries(I).filter(([t])=>t!==e)):I,n=4,l=u.colorNodes.filter(e=>!Object.values(o).includes(e.color)||e.color===i).findIndex(e=>e.id===r.id),d={x:50+200*(l%n),y:20+80*Math.floor(l/n)};S(e=>e.map(e=>e.id===t.id?{...e,position:d}:e))}B.current=null}else{B.current&&z(e=>{const t={...e};return delete t[B.current],t});const e=u.colorNodes.find(e=>e.color===i);if(e){const r=B.current?Object.fromEntries(Object.entries(I).filter(([e])=>e!==B.current)):I,o=4,n=u.colorNodes.filter(e=>!Object.values(r).includes(e.color)||e.color===i).findIndex(t=>t.id===e.id),l={x:50+200*(n%o),y:20+80*Math.floor(n/o)};S(e=>e.map(e=>e.id===t.id?{...e,position:l}:e))}B.current=null}V.current=!1},[L,I,u.colorNodes,S]),q=l(()=>{if(!u.colorNodes||0===u.colorNodes.length)return[];const e=u.colorNodes.filter(e=>!Object.values(I).includes(e.color));return e.map((e,t)=>{const i=Math.floor(t/4),r=t%4;return{id:e.id,type:"clientColorNode",position:{x:50+200*r,y:20+80*i},data:{color:e.color,isEditMode:!1,isConnected:!1,displayMode:"compact",onRemove:()=>{const t=Object.keys(I).find(t=>I[t]===e.color);t&&z(e=>{const i={...e};return delete i[t],i})}},draggable:!0}})},[u.colorNodes,I]),A=l(()=>!b||v.width<=0?[]:u.regionNodes.filter(e=>I[e.id]).map(e=>{const t=I[e.id];if(!u.colorNodes.find(e=>e.color===t))return null;const i=k.current?.clientWidth||0,r=(i>0&&v.width>0?Math.max(50,(i-v.width)/2):50)+e.x/100*v.width,o=200+e.y/100*v.height,n=e.width/100*v.width,l=e.height/100*v.height,d=Math.max(n,60),s=Math.max(l,40);return{id:`color-in-region-${e.id}`,type:"clientColorNode",position:{x:r,y:o},data:{color:t,isEditMode:!1,isConnected:!0,regionId:e.id,onRemove:()=>H(e.id)},draggable:!0,width:d,height:s,style:{width:`${d}px`,height:`${s}px`,margin:0,padding:0,boxSizing:"border-box"}}}).filter(e=>null!==e),[b,v,u.regionNodes,u.colorNodes,I,H]),G=l(()=>{if(!b||v.width<=0)return[];const e=k.current?.clientWidth||0,t=e>0?(e-v.width)/2:50;return u.regionNodes.map(e=>{const i=I[e.id]||"";return{id:e.id,type:"clientColorRegionNode",position:{x:t+e.x/100*v.width,y:200+e.y/100*v.height},data:{color:e.color,x:e.x,y:e.y,width:e.width,height:e.height,isEditMode:!1,isClientMode:!0,savedColor:i,onFillColor:t=>D(e.id,t),onRemove:()=>H(e.id),isHighlighted:W===e.id},draggable:!1,style:{width:e.width/100*v.width+"px",height:e.height/100*v.height+"px",pointerEvents:i?"none":"auto"},zIndex:0}})},[b,v,u.regionNodes,I,D,H,W]);return o(()=>{if(V.current)return;const e=[...G,...q,...A];S(e)},[q,A,G,S,b,v]),t("div",{className:"space-y-4",children:[e("div",{className:"flex items-center justify-between",children:t("div",{children:[e("h2",{className:"text-lg font-semibold text-gray-900",children:N.clientTitle}),e("p",{className:"text-sm text-gray-500",children:N.clientSubtitle})]})}),u.instructions&&u.instructions.length>0&&t("div",{className:"overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm",children:[t("div",{className:"flex items-center gap-2 border-b border-gray-100 bg-gradient-to-r from-blue-50 to-indigo-50 px-4 py-2.5",children:[e("svg",{className:"h-5 w-5 text-blue-600",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"})}),e("h3",{className:"text-sm font-semibold text-gray-800",children:"Hướng dẫn"})]}),e("div",{className:"divide-y divide-gray-50 mt-2",children:u.instructions.map((i,r)=>e("div",{className:"flex items-center gap-3 px-5 py-2 transition-colors hover:bg-gray-50",children:t("span",{className:"text-sm font-semibold text-gray-700",children:[r+1,". ",i]})},r))})]}),t("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-lg",children:[e("div",{className:"border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3",children:t("div",{className:"flex items-center gap-2",children:[e("div",{className:"h-2.5 w-2.5 rounded-full bg-red-400"}),e("div",{className:"h-2.5 w-2.5 rounded-full bg-yellow-400"}),e("div",{className:"h-2.5 w-2.5 rounded-full bg-green-400"}),e("span",{className:"ml-3 text-sm font-medium text-gray-500",children:"Workspace"})]})}),t("div",{className:"relative bg-gradient-to-br from-slate-50 to-gray-100",style:{height:"800px",width:"100%"},ref:k,children:[e("div",{className:"pointer-events-none absolute",style:{left:`${P}px`,top:"200px",width:v.width?`${v.width}px`:"auto",height:v.height?`${v.height}px`:"auto",zIndex:0},children:j?e("div",{className:"flex h-full w-full items-center justify-center text-gray-500",children:"Đang tải hình ảnh..."}):M?e("img",{ref:O,src:M,alt:"Background",className:"h-full w-full object-contain",style:v.width&&v.height?{width:`${v.width}px`,height:`${v.height}px`}:void 0,onLoad:()=>{if(O.current&&k.current){const e=k.current.clientWidth-100,t=k.current.clientHeight-260,i=O.current.naturalWidth||O.current.clientWidth,r=O.current.naturalHeight||O.current.clientHeight,o=e/i,n=t/r,l=Math.min(o,n,1);C({width:i*l,height:r*l}),w(!0)}},onError:()=>{d.error("Không thể load ảnh!"),w(!1)}}):e("div",{className:"flex h-full w-full items-center justify-center text-gray-500",children:"Không có hình ảnh"})}),e(s,{nodes:R,edges:[],onNodesChange:$,onNodeDragStart:K,onNodeDrag:T,onNodeDragStop:U,onEdgesChange:E,nodeTypes:y,defaultViewport:{x:0,y:0,zoom:1},fitView:!1,nodesDraggable:!0,nodesConnectable:!1,elementsSelectable:!1,panOnDrag:!1,panOnScroll:!1,zoomOnScroll:!1,zoomOnDoubleClick:!1,zoomOnPinch:!1,preventScrolling:!0,autoPanOnNodeDrag:!1,autoPanOnConnect:!1,proOptions:{hideAttribution:!0},style:{width:"100%",height:"100%",position:"absolute",top:0,left:0,zIndex:10},children:e(a,{})})]})]}),p&&f&&e("div",{className:"rounded-xl bg-gradient-to-r from-blue-50 to-indigo-50 p-4 border border-blue-200",children:t("div",{className:"flex items-start gap-3",children:[e("div",{className:"flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-white",children:e(m,{className:"h-3.5 w-3.5"})}),t("div",{className:"flex-1",children:[e("span",{className:"text-xs font-semibold text-blue-700 uppercase tracking-wider",children:"Giải thích"}),e("p",{className:"mt-1 text-sm text-gray-700 leading-relaxed",children:f})]})]})})]})}export function ColorRegionClient(t){return e(u,{children:e(N,{...t})})}