@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.2

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 (485) 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.js +1 -98
  4. package/dist/api/exam-questions.types.js +1 -1
  5. package/dist/api/exam-taking.js +1 -73
  6. package/dist/api/exam-taking.types.js +1 -1
  7. package/dist/api/index.js +1 -6
  8. package/dist/api/result-review.js +1 -50
  9. package/dist/api/result-review.types.js +1 -1
  10. package/dist/components/CueCardEditor.js +1 -58
  11. package/dist/components/common/ResolvedImage.js +1 -15
  12. package/dist/components/exams/CreateExamInfoForm.js +1 -35
  13. package/dist/components/exams/CreateExamPageContainer.js +1 -103
  14. package/dist/components/exams/ExamCreator.js +1 -89
  15. package/dist/components/exams/ExamPartTabs.js +1 -40
  16. package/dist/components/exams/ExamQuestionGrid.js +1 -18
  17. package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
  18. package/dist/components/exams/PartAudioUploader.js +1 -9
  19. package/dist/components/exams/TemplateSelector.js +1 -66
  20. package/dist/components/exams/api.js +1 -3
  21. package/dist/components/exams/create-exam.validation.js +1 -18
  22. package/dist/components/exams/exam-template.utils.js +1 -18
  23. package/dist/components/exams/index.js +1 -10
  24. package/dist/components/exams/take/ExamTakingPageContainer.js +1 -153
  25. package/dist/components/exams/take/GenericQuestionRenderer.js +1 -14
  26. package/dist/components/exams/take/exam-taking.utils.js +1 -88
  27. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -24
  28. package/dist/components/exams/take/index.js +1 -2
  29. package/dist/components/exams/take/types.js +1 -1
  30. package/dist/components/index.js +1 -42
  31. package/dist/components/questions/_shared/config/question-types.config.js +1 -333
  32. package/dist/components/questions/_shared/constants/color-palette.js +1 -21
  33. package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +1 -105
  34. package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +1 -222
  35. package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +1 -140
  36. package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +1 -123
  37. package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +1 -50
  38. package/dist/components/questions/_shared/hooks/useQuestionFormState.js +1 -54
  39. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +1 -123
  40. package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +1 -121
  41. package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +1 -70
  42. package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +1 -194
  43. package/dist/components/questions/_shared/index.js +1 -7
  44. package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
  45. package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
  46. package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +1 -3
  47. package/dist/components/questions/_shared/types/color-region.config.js +1 -1
  48. package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
  49. package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
  50. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
  51. package/dist/components/questions/_shared/types/index.js +1 -12
  52. package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
  53. package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
  54. package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
  55. package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
  56. package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
  57. package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
  58. package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
  59. package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
  60. package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +1 -3
  61. package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
  62. package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +1 -3
  63. package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
  64. package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
  65. package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
  66. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
  67. package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
  68. package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
  69. package/dist/components/questions/_shared/types/question-group.type.js +1 -11
  70. package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
  71. package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
  72. package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
  73. package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
  74. package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
  75. package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
  76. package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
  77. package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
  78. package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
  79. package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
  80. package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +1 -3
  81. package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
  82. package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
  83. package/dist/components/questions/_shared/utils/answer-builder.js +1 -79
  84. package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +1 -39
  85. package/dist/components/questions/_shared/utils/id-generator.js +1 -3
  86. package/dist/components/questions/_shared/utils/question-validation.js +1 -21
  87. package/dist/components/questions/components/ColorNode.js +1 -85
  88. package/dist/components/questions/components/ColorRegionClient.js +1 -545
  89. package/dist/components/questions/components/ColorRegionCreator.js +1 -420
  90. package/dist/components/questions/components/ColorRegionNode.js +1 -59
  91. package/dist/components/questions/components/CreatorGuide.js +1 -103
  92. package/dist/components/questions/components/NameNode.js +1 -26
  93. package/dist/components/questions/components/QuestionBankOption.js +1 -46
  94. package/dist/components/questions/components/QuestionBasicInfoForm.js +1 -9
  95. package/dist/components/questions/components/QuestionCommonFields.js +1 -33
  96. package/dist/components/questions/components/QuestionSearchDropdown.js +1 -258
  97. package/dist/components/questions/components/QuestionTypeSelector.js +1 -32
  98. package/dist/components/questions/components/QuestionTypeSpecificForms.js +1 -6
  99. package/dist/components/questions/components/RegionNode.js +1 -94
  100. package/dist/components/questions/components/index.js +1 -14
  101. package/dist/components/questions/creator/QuestionCreator.js +1 -90
  102. package/dist/components/questions/creator/QuestionGroupCreator.js +1 -119
  103. package/dist/components/questions/creator/dedicated-component-router.js +1 -79
  104. package/dist/components/questions/creator/index.js +1 -3
  105. package/dist/components/questions/creator/question-type-registry.js +1 -80
  106. package/dist/components/questions/groups/ArticlesGroupCard.js +1 -121
  107. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -50
  108. package/dist/components/questions/groups/DocumentGroupCard.js +1 -114
  109. package/dist/components/questions/groups/MatchAnswerGroupCard.js +1 -25
  110. package/dist/components/questions/index.js +1 -10
  111. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +1 -37
  112. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +1 -351
  113. package/dist/components/questions/types/answer-the-question/register.js +1 -67
  114. package/dist/components/questions/types/answer-the-question/transform.js +1 -36
  115. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +1 -141
  116. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +1 -118
  117. package/dist/components/questions/types/arrange-letters-into-words/index.js +1 -2
  118. package/dist/components/questions/types/arrange-letters-into-words/register.js +1 -4
  119. package/dist/components/questions/types/arrange-letters-into-words/transform.js +1 -24
  120. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -64
  121. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +1 -169
  122. package/dist/components/questions/types/choose-correct-adjective/index.js +1 -2
  123. package/dist/components/questions/types/choose-correct-adjective/register.js +1 -55
  124. package/dist/components/questions/types/choose-correct-adjective/transform.js +1 -16
  125. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -131
  126. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -581
  127. package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -2
  128. package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -101
  129. package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -43
  130. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +1 -275
  131. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -448
  132. package/dist/components/questions/types/fill-in-blank/index.js +1 -2
  133. package/dist/components/questions/types/fill-in-blank/register.js +1 -138
  134. package/dist/components/questions/types/fill-in-blank/transform.js +1 -59
  135. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +1 -117
  136. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +1 -671
  137. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +1 -2
  138. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +1 -37
  139. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +1 -15
  140. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +1 -262
  141. package/dist/components/questions/types/gn-speaking-interview/register.js +1 -121
  142. package/dist/components/questions/types/gn-speaking-interview/transform.js +1 -48
  143. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +1 -16
  144. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +1 -27
  145. package/dist/components/questions/types/image-object-matching/index.js +1 -2
  146. package/dist/components/questions/types/image-object-matching/register.js +1 -3
  147. package/dist/components/questions/types/image-object-matching/transform.js +1 -2
  148. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +1 -102
  149. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +1 -344
  150. package/dist/components/questions/types/label-the-picture/index.js +1 -2
  151. package/dist/components/questions/types/label-the-picture/register.js +1 -54
  152. package/dist/components/questions/types/label-the-picture/transform.js +1 -28
  153. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +1 -78
  154. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +1 -232
  155. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +1 -2
  156. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +1 -82
  157. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +1 -27
  158. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +1 -70
  159. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +1 -386
  160. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +1 -2
  161. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +1 -100
  162. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +1 -33
  163. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +1 -106
  164. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +1 -457
  165. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +1 -2
  166. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +1 -110
  167. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +1 -35
  168. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +1 -8
  169. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -22
  170. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +1 -291
  171. package/dist/components/questions/types/listen-and-speak-answer/index.js +1 -2
  172. package/dist/components/questions/types/listen-and-speak-answer/register.js +1 -84
  173. package/dist/components/questions/types/listen-and-speak-answer/transform.js +1 -27
  174. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -32
  175. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +1 -295
  176. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +1 -79
  177. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +1 -27
  178. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -34
  179. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +1 -285
  180. package/dist/components/questions/types/listen-and-speak-image-group/register.js +1 -81
  181. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +1 -26
  182. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -38
  183. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +1 -385
  184. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +1 -87
  185. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +1 -32
  186. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -26
  187. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +1 -247
  188. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +1 -59
  189. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +1 -18
  190. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -26
  191. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +1 -256
  192. package/dist/components/questions/types/listen-and-speak-question-list/register.js +1 -81
  193. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +1 -26
  194. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -26
  195. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +1 -230
  196. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +1 -56
  197. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +1 -16
  198. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +1 -72
  199. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +1 -230
  200. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  201. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +1 -66
  202. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +1 -42
  203. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +1 -96
  204. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +1 -238
  205. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +1 -2
  206. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +1 -76
  207. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +1 -19
  208. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +1 -134
  209. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +1 -617
  210. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +1 -2
  211. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +1 -82
  212. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +1 -29
  213. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +1 -38
  214. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +1 -359
  215. package/dist/components/questions/types/match-by-writing-answer/index.js +1 -3
  216. package/dist/components/questions/types/match-by-writing-answer/register.js +1 -58
  217. package/dist/components/questions/types/match-by-writing-answer/transform.js +1 -61
  218. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +1 -16
  219. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +1 -23
  220. package/dist/components/questions/types/read-and-color-objects/index.js +1 -2
  221. package/dist/components/questions/types/read-and-color-objects/register.js +1 -3
  222. package/dist/components/questions/types/read-and-color-objects/transform.js +1 -36
  223. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +1 -125
  224. package/dist/components/questions/types/read-displayed-content/register.js +1 -64
  225. package/dist/components/questions/types/read-displayed-content/transform.js +1 -12
  226. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -64
  227. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +1 -511
  228. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +1 -2
  229. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +1 -58
  230. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +1 -28
  231. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +1 -373
  232. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +1 -41
  233. package/dist/components/questions/types/speaking-conversation/register.js +1 -110
  234. package/dist/components/questions/types/speaking-conversation/transform.js +1 -34
  235. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +1 -74
  236. package/dist/components/questions/types/speaking-cue-card/register.js +1 -52
  237. package/dist/components/questions/types/speaking-cue-card/transform.js +1 -12
  238. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -10
  239. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +1 -161
  240. package/dist/components/questions/types/speaking-describe-image/index.js +1 -2
  241. package/dist/components/questions/types/speaking-describe-image/register.js +1 -69
  242. package/dist/components/questions/types/speaking-describe-image/transform.js +1 -16
  243. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +1 -234
  244. package/dist/components/questions/types/spontaneous-qa/register.js +1 -93
  245. package/dist/components/questions/types/spontaneous-qa/transform.js +1 -34
  246. package/dist/components/questions/types/true-false/TrueFalseClient.js +1 -75
  247. package/dist/components/questions/types/true-false/TrueFalseCreator.js +1 -244
  248. package/dist/components/questions/types/true-false/index.js +1 -2
  249. package/dist/components/questions/types/true-false/register.js +1 -77
  250. package/dist/components/questions/types/true-false/transform.js +1 -32
  251. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +1 -233
  252. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +1 -631
  253. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +1 -28
  254. package/dist/components/questions/types/word-fill-paragraph/index.js +1 -2
  255. package/dist/components/questions/types/word-fill-paragraph/register.js +1 -136
  256. package/dist/components/questions/types/word-fill-paragraph/transform.js +1 -56
  257. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -138
  258. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -261
  259. package/dist/components/questions/types/word-fill-structured-form/index.js +1 -2
  260. package/dist/components/questions/types/word-fill-structured-form/register.js +1 -67
  261. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -40
  262. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -84
  263. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +1 -192
  264. package/dist/components/questions/types/word-ordering/index.js +1 -2
  265. package/dist/components/questions/types/word-ordering/register.js +1 -46
  266. package/dist/components/questions/types/word-ordering/transform.js +1 -24
  267. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -98
  268. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +1 -320
  269. package/dist/components/questions/types/write-a-short-letter/index.js +1 -2
  270. package/dist/components/questions/types/write-a-short-letter/register.js +1 -68
  271. package/dist/components/questions/types/write-a-short-letter/transform.js +1 -25
  272. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +1 -61
  273. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +1 -196
  274. package/dist/components/questions/types/write-correct-verb-form/index.js +1 -2
  275. package/dist/components/questions/types/write-correct-verb-form/register.js +1 -62
  276. package/dist/components/questions/types/write-correct-verb-form/transform.js +1 -17
  277. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +1 -22
  278. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +1 -174
  279. package/dist/components/questions/types/write-sentences/index.js +1 -2
  280. package/dist/components/questions/types/write-sentences/register.js +1 -58
  281. package/dist/components/questions/types/write-sentences/transform.js +1 -28
  282. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -96
  283. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +1 -161
  284. package/dist/components/questions/types/write-short-paragraph/index.js +1 -2
  285. package/dist/components/questions/types/write-short-paragraph/register.js +1 -4
  286. package/dist/components/questions/types/write-short-paragraph/transform.js +1 -21
  287. package/dist/components/questions/viewer/QuestionGroupViewer.js +1 -36
  288. package/dist/components/questions/viewer/QuestionViewer.js +1 -446
  289. package/dist/components/questions/viewer/index.js +1 -2
  290. package/dist/components/results/ResultReviewPageContainer.js +1 -98
  291. package/dist/components/results/ReviewQuestionRenderer.js +1 -72
  292. package/dist/components/results/index.js +1 -2
  293. package/dist/components/results/result-review.utils.js +1 -6
  294. package/dist/components/results/review-data.js +1 -150
  295. package/dist/components/results/review-renderer.utils.js +1 -97
  296. package/dist/components/results/review-types.js +1 -1
  297. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -138
  298. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +1 -12
  299. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +1 -18
  300. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +1 -11
  301. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +1 -11
  302. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +1 -9
  303. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +1 -13
  304. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +1 -8
  305. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +1 -6
  306. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +1 -5
  307. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +1 -9
  308. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -41
  309. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +1 -25
  310. package/dist/components/themes/cambridge-yle/index.js +1 -13
  311. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -24
  312. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -88
  313. package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -55
  314. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -156
  315. package/dist/components/themes/english-certification/EcWritingTask.js +1 -34
  316. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -114
  317. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +1 -11
  318. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -21
  319. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +1 -15
  320. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +1 -19
  321. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -148
  322. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +1 -31
  323. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +1 -71
  324. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +1 -33
  325. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +1 -16
  326. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -22
  327. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +1 -37
  328. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -72
  329. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -90
  330. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +1 -18
  331. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -29
  332. package/dist/components/themes/english-certification/english-certification-theme.js +1 -58
  333. package/dist/components/themes/english-certification/grouped-question-utils.js +1 -218
  334. package/dist/components/themes/english-certification/index.js +1 -21
  335. package/dist/components/themes/index.js +1 -6
  336. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +1 -170
  337. package/dist/components/themes/summer-sky/SummerSkyLayout.js +1 -221
  338. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +1 -18
  339. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +1 -194
  340. package/dist/components/themes/types.js +1 -1
  341. package/dist/components/ui/alert-dialog.js +1 -26
  342. package/dist/components/ui/alert.js +1 -22
  343. package/dist/components/ui/avatar.js +1 -12
  344. package/dist/components/ui/badge.js +1 -38
  345. package/dist/components/ui/button.js +1 -35
  346. package/dist/components/ui/calendar.js +1 -76
  347. package/dist/components/ui/card.js +1 -16
  348. package/dist/components/ui/carousel.js +1 -91
  349. package/dist/components/ui/checkbox.js +1 -9
  350. package/dist/components/ui/collapsible.js +1 -13
  351. package/dist/components/ui/command.js +1 -34
  352. package/dist/components/ui/confirm-dialog.js +1 -49
  353. package/dist/components/ui/dialog.js +1 -23
  354. package/dist/components/ui/drawer.js +1 -35
  355. package/dist/components/ui/dropdown-menu.js +1 -33
  356. package/dist/components/ui/file-upload.js +1 -155
  357. package/dist/components/ui/form.js +1 -62
  358. package/dist/components/ui/hover-card.js +1 -10
  359. package/dist/components/ui/image-preview.js +1 -29
  360. package/dist/components/ui/input.js +1 -8
  361. package/dist/components/ui/label.js +1 -9
  362. package/dist/components/ui/language-switcher.js +1 -15
  363. package/dist/components/ui/multi-select.js +1 -90
  364. package/dist/components/ui/points-input.js +1 -33
  365. package/dist/components/ui/popover.js +1 -26
  366. package/dist/components/ui/progress.js +1 -16
  367. package/dist/components/ui/radio-group.js +1 -12
  368. package/dist/components/ui/scroll-area.js +1 -11
  369. package/dist/components/ui/select.js +1 -38
  370. package/dist/components/ui/separator.js +1 -8
  371. package/dist/components/ui/sheet.js +1 -40
  372. package/dist/components/ui/single-select.js +1 -76
  373. package/dist/components/ui/skeleton.js +1 -6
  374. package/dist/components/ui/switch.js +1 -8
  375. package/dist/components/ui/table.js +1 -20
  376. package/dist/components/ui/tabs.js +1 -17
  377. package/dist/components/ui/textarea.js +1 -8
  378. package/dist/components/ui/tooltip.js +1 -11
  379. package/dist/index.js +1 -19
  380. package/dist/mcp/config.js +1 -32
  381. package/dist/mcp/http-client.js +1 -48
  382. package/dist/mcp/index.js +1 -29
  383. package/dist/mcp/tools/_helpers.js +1 -25
  384. package/dist/mcp/tools/exams.js +1 -439
  385. package/dist/mcp/tools/index.js +1 -6
  386. package/dist/mcp/tools/results.js +1 -19
  387. package/dist/shared/constants/ApiConstant.js +1 -276
  388. package/dist/shared/constants/QueryKeyConstant.js +1 -112
  389. package/dist/shared/constants/exam-level.enum.js +1 -41
  390. package/dist/shared/constants/landings/placement-test.constants.js +1 -431
  391. package/dist/shared/constants/question-skills.js +1 -267
  392. package/dist/shared/constants/routes.js +1 -81
  393. package/dist/shared/index.js +1 -7
  394. package/dist/shared/lib/hooks/index.js +1 -10
  395. package/dist/shared/lib/hooks/useAlertDialog.js +1 -57
  396. package/dist/shared/lib/hooks/useAudioPlayCounter.js +1 -30
  397. package/dist/shared/lib/hooks/useAudioPreview.js +1 -81
  398. package/dist/shared/lib/hooks/useAudioUpload.js +1 -24
  399. package/dist/shared/lib/hooks/useConfirmDialog.js +1 -89
  400. package/dist/shared/lib/hooks/useDebouncedCallback.js +1 -32
  401. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +1 -12
  402. package/dist/shared/lib/hooks/useFileUpload.js +1 -24
  403. package/dist/shared/lib/hooks/useImageUrl.js +1 -5
  404. package/dist/shared/lib/hooks/usePresignedFileUrl.js +1 -10
  405. package/dist/shared/lib/hooks/useReactHookForm.js +1 -31
  406. package/dist/shared/lib/hooks/useToast.js +1 -62
  407. package/dist/shared/lib/i18n/index.js +1 -54
  408. package/dist/shared/lib/i18n/messages/en/admin.js +1 -2104
  409. package/dist/shared/lib/i18n/messages/en/auth.js +1 -86
  410. package/dist/shared/lib/i18n/messages/en/common.js +1 -140
  411. package/dist/shared/lib/i18n/messages/en/file-upload.js +1 -18
  412. package/dist/shared/lib/i18n/messages/en/student.js +1 -133
  413. package/dist/shared/lib/i18n/messages/en/terms.js +1 -37
  414. package/dist/shared/lib/i18n/messages/en.js +1 -15
  415. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -2104
  416. package/dist/shared/lib/i18n/messages/vi/auth.js +1 -86
  417. package/dist/shared/lib/i18n/messages/vi/common.js +1 -140
  418. package/dist/shared/lib/i18n/messages/vi/file-upload.js +1 -18
  419. package/dist/shared/lib/i18n/messages/vi/student.js +1 -133
  420. package/dist/shared/lib/i18n/messages/vi/terms.js +1 -37
  421. package/dist/shared/lib/i18n/messages/vi.js +1 -15
  422. package/dist/shared/lib/i18n/types.js +1 -1
  423. package/dist/shared/lib/stores/examQuestionStore.js +1 -212
  424. package/dist/shared/lib/stores/localeStore.js +1 -10
  425. package/dist/shared/lib/utils/public-page.js +1 -44
  426. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  427. package/dist/shared/lib/utils/question-transform.js +1 -174
  428. package/dist/shared/lib/utils.js +1 -168
  429. package/dist/shared/lib/validation/choose-correct-answer.validation.js +1 -111
  430. package/dist/shared/lib/validation/label-the-picture.validation.js +1 -19
  431. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +1 -46
  432. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +1 -21
  433. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +1 -34
  434. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +1 -40
  435. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +1 -45
  436. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +1 -19
  437. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +1 -39
  438. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +1 -32
  439. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +1 -19
  440. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +1 -28
  441. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +1 -12
  442. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +1 -47
  443. package/dist/shared/lib/validation/read-and-color-objects.validation.js +1 -29
  444. package/dist/shared/lib/validation/read-displayed-content.validation.js +1 -28
  445. package/dist/shared/lib/validation/speaking-conversation.validation.js +1 -75
  446. package/dist/shared/lib/validation/speaking-describe-image.validation.js +1 -31
  447. package/dist/shared/lib/validation/spontaneous-qa.validation.js +1 -48
  448. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +1 -38
  449. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +1 -39
  450. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +1 -43
  451. package/dist/shared/lib/validation/word-ordering.validation.js +1 -33
  452. package/dist/shared/types/branch.types.js +1 -6
  453. package/dist/shared/types/common.types.js +1 -50
  454. package/dist/shared/types/entities/exam-schedule.types.js +1 -5
  455. package/dist/shared/types/entities/exam.types.js +1 -1
  456. package/dist/shared/types/entities/question.types.js +1 -4
  457. package/dist/shared/types/entities/result.types.js +1 -4
  458. package/dist/shared/types/entities/user.types.js +1 -4
  459. package/dist/shared/types/exam-taking.types.js +1 -2
  460. package/dist/shared/types/history.types.js +1 -34
  461. package/dist/shared/types/index.js +1 -1
  462. package/dist/shared/types/questions/choose-correct-adjective.js +1 -4
  463. package/dist/shared/types/questions/choose-the-correct-answer.js +1 -9
  464. package/dist/shared/types/questions/fill-missing-words-in-grid.js +1 -11
  465. package/dist/shared/types/questions/index.js +1 -40
  466. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  467. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  468. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  469. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +1 -3
  470. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +1 -3
  471. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  472. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +1 -3
  473. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +1 -5
  474. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +1 -9
  475. package/dist/shared/types/questions/look-picture-fill-word-hint.js +1 -7
  476. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  477. package/dist/shared/types/questions/speaking-conversation.js +1 -7
  478. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  479. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  480. package/dist/shared/types/questions/write-correct-verb-form.js +1 -12
  481. package/dist/shared/types/role.types.js +1 -1
  482. package/dist/shared/types/structure-management.js +1 -28
  483. package/dist/shared/types/student.types.js +1 -54
  484. package/dist/shared/types/teacher-dashboard-rooms.js +1 -2
  485. package/package.json +4 -2
@@ -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})})}