@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,155 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useRef, useState, useCallback } from 'react';
4
- import { Button } from '../../components/ui/button';
5
- import { Input } from '../../components/ui/input';
6
- import { Label } from '../../components/ui/label';
7
- import { Upload, X, Play, Square, Loader2 } from 'lucide-react';
8
- import { toast } from 'sonner';
9
- import { useFileUpload } from '../../shared/lib/hooks/useFileUpload';
10
- import { useAudioUpload } from '../../shared/lib/hooks/useAudioUpload';
11
- import { usePresignedFileUrl } from '../../shared/lib/hooks/usePresignedFileUrl';
12
- import { t } from '../../shared/lib/i18n';
13
- export function FileUpload({ label, accept, value, onChange, onPresignedUrlChange, disabled = false, maxSize = 10, placeholder = 'Paste URL or upload file', icon, required = false, id, onFileLoad, showPlayButton = false, onPlay, autoUpload = false, prefix, examId, partId, }) {
14
- const fileInputRef = useRef(null);
15
- const audioRef = useRef(null);
16
- const [presignedUrl, setPresignedUrl] = useState(''); // Store presignedUrl for preview
17
- const [isPlayingAudio, setIsPlayingAudio] = useState(false);
18
- // Determine if we should use the audio-specific upload API
19
- const useAudioApi = Boolean(examId);
20
- // Determine if value is an S3 key (not blob, not http) to fetch presigned URL for playback
21
- const isS3Key = Boolean(value && !value.startsWith('blob:') && !value.startsWith('http'));
22
- const { previewUrl: fetchedPresignedUrl } = usePresignedFileUrl(showPlayButton && isS3Key && !presignedUrl ? value : undefined);
23
- // Resolve the best available playable URL for audio
24
- const playableAudioUrl = presignedUrl || fetchedPresignedUrl || (value?.startsWith('http') || value?.startsWith('blob:') ? value : '');
25
- // Built-in audio play/stop handler when no external onPlay is provided
26
- const handleInternalPlay = useCallback(() => {
27
- if (!playableAudioUrl) {
28
- toast.error(t('fileUpload.audio.noUrl'));
29
- return;
30
- }
31
- if (isPlayingAudio && audioRef.current) {
32
- audioRef.current.pause();
33
- audioRef.current.currentTime = 0;
34
- setIsPlayingAudio(false);
35
- return;
36
- }
37
- // Create or reuse audio element
38
- if (!audioRef.current || audioRef.current.src !== playableAudioUrl) {
39
- if (audioRef.current) {
40
- audioRef.current.pause();
41
- }
42
- const audio = new Audio(playableAudioUrl);
43
- audio.addEventListener('ended', () => setIsPlayingAudio(false));
44
- audio.addEventListener('error', () => {
45
- toast.error(t('fileUpload.audio.playError'));
46
- setIsPlayingAudio(false);
47
- });
48
- audioRef.current = audio;
49
- }
50
- audioRef.current.play()
51
- .then(() => setIsPlayingAudio(true))
52
- .catch(() => {
53
- toast.error(t('fileUpload.audio.playError'));
54
- setIsPlayingAudio(false);
55
- });
56
- }, [playableAudioUrl, isPlayingAudio]);
57
- const { uploadFile, isUploading: isUploadingFile } = useFileUpload({
58
- prefix,
59
- onSuccess: (key, presignedUrl) => {
60
- // Save key (for API), store presignedUrl (for preview)
61
- onChange(key);
62
- if (presignedUrl) {
63
- setPresignedUrl(presignedUrl);
64
- onPresignedUrlChange?.(presignedUrl);
65
- }
66
- onFileLoad?.();
67
- toast.success(t('fileUpload.success'));
68
- },
69
- onError: () => {
70
- // Error handling is done in the hook
71
- },
72
- });
73
- const { uploadAudio, isUploading: isUploadingAudio } = useAudioUpload({
74
- examId: examId || '',
75
- partId,
76
- onSuccess: (key, presignedUrl) => {
77
- onChange(key);
78
- if (presignedUrl) {
79
- setPresignedUrl(presignedUrl);
80
- onPresignedUrlChange?.(presignedUrl);
81
- }
82
- onFileLoad?.();
83
- toast.success(t('fileUpload.audio.success'));
84
- },
85
- onError: () => {
86
- // Error handling is done in the hook
87
- },
88
- });
89
- const isUploading = useAudioApi ? isUploadingAudio : isUploadingFile;
90
- const handleFileUpload = async (e) => {
91
- const file = e.target.files?.[0];
92
- if (!file)
93
- return;
94
- // Validate file type
95
- const acceptedTypes = accept.split(',').map((t) => t.trim());
96
- const fileType = file.type;
97
- const isValid = acceptedTypes.some((type) => {
98
- if (type.endsWith('/*')) {
99
- const prefix = type.split('/')[0];
100
- return fileType.startsWith(prefix + '/');
101
- }
102
- return fileType === type;
103
- });
104
- if (!isValid) {
105
- toast.error(t('fileUpload.invalidFormat', { accept }));
106
- return;
107
- }
108
- // Validate file size
109
- const fileSizeMB = file.size / (1024 * 1024);
110
- if (fileSizeMB > maxSize) {
111
- toast.error(t('fileUpload.tooLarge', { maxSize }));
112
- return;
113
- }
114
- if (autoUpload) {
115
- if (useAudioApi) {
116
- // Use dedicated audio upload API
117
- await uploadAudio(file);
118
- }
119
- else {
120
- // Use generic file upload API
121
- await uploadFile(file);
122
- }
123
- }
124
- else {
125
- // Create object URL for preview (local only)
126
- const url = URL.createObjectURL(file);
127
- onChange(url);
128
- onFileLoad?.(); // Call the callback if provided
129
- toast.success(t('fileUpload.uploaded', { name: file.name }));
130
- }
131
- };
132
- const handleClear = () => {
133
- if (value.startsWith('blob:')) {
134
- URL.revokeObjectURL(value);
135
- }
136
- // Stop audio playback if playing
137
- if (audioRef.current) {
138
- audioRef.current.pause();
139
- audioRef.current = null;
140
- setIsPlayingAudio(false);
141
- }
142
- onChange('');
143
- setPresignedUrl('');
144
- onPresignedUrlChange?.(''); // Also notify parent to clear the preview URL
145
- if (fileInputRef.current) {
146
- fileInputRef.current.value = '';
147
- }
148
- };
149
- const displayValue = value.startsWith('blob:') ? 'Tải lên file' : value;
150
- const inputId = id || label.toLowerCase().replace(/\s+/g, '-');
151
- // If value exists and is not a blob URL, it means file has been uploaded (has key)
152
- // Disable input editing in this case, only allow clearing
153
- const isUploaded = Boolean(value && !value.startsWith('blob:') && !value.startsWith('http'));
154
- return (_jsxs("div", { children: [_jsxs(Label, { htmlFor: inputId, children: [label, " ", required && _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx("div", { className: "mt-1 space-y-2", children: _jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { id: inputId, value: displayValue, onChange: (e) => onChange(e.target.value), placeholder: placeholder, disabled: disabled || isUploaded, readOnly: isUploaded }), _jsx("input", { ref: fileInputRef, type: "file", accept: accept, onChange: handleFileUpload, className: "hidden", disabled: disabled }), _jsx(Button, { variant: "outline", size: "icon", disabled: disabled || isUploading, onClick: () => fileInputRef.current?.click(), title: "T\u1EA3i l\u00EAn file", children: isUploading ? (_jsx(Loader2, { className: "h-4 w-4 animate-spin" })) : (icon || _jsx(Upload, { className: "h-4 w-4" })) }), showPlayButton && value && (_jsx(Button, { variant: "outline", size: "icon", onClick: onPlay || handleInternalPlay, title: isPlayingAudio ? "Dừng âm thanh" : "Phát âm thanh", disabled: disabled, children: isPlayingAudio ? _jsx(Square, { className: "h-4 w-4" }) : _jsx(Play, { className: "h-4 w-4" }) })), value && (_jsx(Button, { variant: "outline", size: "icon", onClick: handleClear, title: "Clear", disabled: disabled, children: _jsx(X, { className: "h-4 w-4" }) }))] }) })] }));
155
- }
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useRef as t,useState as i,useCallback as a}from"react";import{Button as o}from"../../components/ui/button";import{Input as l}from"../../components/ui/input";import{Label as s}from"../../components/ui/label";import{Upload as n,X as c,Play as d,Square as u,Loader2 as p}from"lucide-react";import{toast as m}from"sonner";import{useFileUpload as h}from"../../shared/lib/hooks/useFileUpload";import{useAudioUpload as f}from"../../shared/lib/hooks/useAudioUpload";import{usePresignedFileUrl as b}from"../../shared/lib/hooks/usePresignedFileUrl";import{t as U}from"../../shared/lib/i18n";export function FileUpload({label:v,accept:g,value:x,onChange:w,onPresignedUrlChange:y,disabled:L=!1,maxSize:W=10,placeholder:C="Paste URL or upload file",icon:k,required:N=!1,id:F,onFileLoad:z,showPlayButton:E=!1,onPlay:P,autoUpload:j=!1,prefix:R,examId:B,partId:I}){const S=t(null),A=t(null),[O,T]=i(""),[q,D]=i(!1),G=Boolean(B),H=Boolean(x&&!x.startsWith("blob:")&&!x.startsWith("http")),{previewUrl:J}=b(E&&H&&!O?x:void 0),K=O||J||(x?.startsWith("http")||x?.startsWith("blob:")?x:""),M=a(()=>{if(K){if(q&&A.current)return A.current.pause(),A.current.currentTime=0,void D(!1);if(!A.current||A.current.src!==K){A.current&&A.current.pause();const e=new Audio(K);e.addEventListener("ended",()=>D(!1)),e.addEventListener("error",()=>{m.error(U("fileUpload.audio.playError")),D(!1)}),A.current=e}A.current.play().then(()=>D(!0)).catch(()=>{m.error(U("fileUpload.audio.playError")),D(!1)})}else m.error(U("fileUpload.audio.noUrl"))},[K,q]),{uploadFile:Q,isUploading:V}=h({prefix:R,onSuccess:(e,r)=>{w(e),r&&(T(r),y?.(r)),z?.(),m.success(U("fileUpload.success"))},onError:()=>{}}),{uploadAudio:X,isUploading:Y}=f({examId:B||"",partId:I,onSuccess:(e,r)=>{w(e),r&&(T(r),y?.(r)),z?.(),m.success(U("fileUpload.audio.success"))},onError:()=>{}}),Z=G?Y:V,$=x.startsWith("blob:")?"Tải lên file":x,_=F||v.toLowerCase().replace(/\s+/g,"-"),ee=Boolean(x&&!x.startsWith("blob:")&&!x.startsWith("http"));return r("div",{children:[r(s,{htmlFor:_,children:[v," ",N&&e("span",{className:"text-red-500",children:"*"})]}),e("div",{className:"mt-1 space-y-2",children:r("div",{className:"flex gap-2",children:[e(l,{id:_,value:$,onChange:e=>w(e.target.value),placeholder:C,disabled:L||ee,readOnly:ee}),e("input",{ref:S,type:"file",accept:g,onChange:async e=>{const r=e.target.files?.[0];if(!r)return;const t=g.split(",").map(e=>e.trim()),i=r.type,a=t.some(e=>{if(e.endsWith("/*")){const r=e.split("/")[0];return i.startsWith(r+"/")}return i===e});if(!a)return void m.error(U("fileUpload.invalidFormat",{accept:g}));if(r.size/1048576>W)m.error(U("fileUpload.tooLarge",{maxSize:W}));else if(j)G?await X(r):await Q(r);else{const e=URL.createObjectURL(r);w(e),z?.(),m.success(U("fileUpload.uploaded",{name:r.name}))}},className:"hidden",disabled:L}),e(o,{variant:"outline",size:"icon",disabled:L||Z,onClick:()=>S.current?.click(),title:"Tải lên file",children:Z?e(p,{className:"h-4 w-4 animate-spin"}):k||e(n,{className:"h-4 w-4"})}),E&&x&&e(o,{variant:"outline",size:"icon",onClick:P||M,title:q?"Dừng âm thanh":"Phát âm thanh",disabled:L,children:e(q?u:d,{className:"h-4 w-4"})}),x&&e(o,{variant:"outline",size:"icon",onClick:()=>{x.startsWith("blob:")&&URL.revokeObjectURL(x),A.current&&(A.current.pause(),A.current=null,D(!1)),w(""),T(""),y?.(""),S.current&&(S.current.value="")},title:"Clear",disabled:L,children:e(c,{className:"h-4 w-4"})})]})})]})}
@@ -1,62 +1 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import * as React from "react";
3
- import { Slot } from "@radix-ui/react-slot";
4
- import { Controller, FormProvider, useFormContext, } from "react-hook-form";
5
- import { cn } from "../../shared/lib/utils";
6
- import { Label } from "../../components/ui/label";
7
- const Form = FormProvider;
8
- const FormFieldContext = React.createContext({});
9
- const FormField = ({ ...props }) => {
10
- return (_jsx(FormFieldContext.Provider, { value: { name: props.name }, children: _jsx(Controller, { ...props }) }));
11
- };
12
- const useFormField = () => {
13
- const fieldContext = React.useContext(FormFieldContext);
14
- const itemContext = React.useContext(FormItemContext);
15
- const { getFieldState, formState } = useFormContext();
16
- const fieldState = getFieldState(fieldContext.name, formState);
17
- if (!fieldContext) {
18
- throw new Error("useFormField should be used within <FormField>");
19
- }
20
- const { id } = itemContext;
21
- return {
22
- id,
23
- name: fieldContext.name,
24
- formItemId: `${id}-form-item`,
25
- formDescriptionId: `${id}-form-item-description`,
26
- formMessageId: `${id}-form-item-message`,
27
- ...fieldState,
28
- };
29
- };
30
- const FormItemContext = React.createContext({});
31
- const FormItem = React.forwardRef(({ className, ...props }, ref) => {
32
- const id = React.useId();
33
- return (_jsx(FormItemContext.Provider, { value: { id }, children: _jsx("div", { ref: ref, className: cn("space-y-2", className), ...props }) }));
34
- });
35
- FormItem.displayName = "FormItem";
36
- const FormLabel = React.forwardRef(({ className, ...props }, ref) => {
37
- const { error, formItemId } = useFormField();
38
- return (_jsx(Label, { ref: ref, className: cn(error && "text-destructive", className), htmlFor: formItemId, ...props }));
39
- });
40
- FormLabel.displayName = "FormLabel";
41
- const FormControl = React.forwardRef(({ ...props }, ref) => {
42
- const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
43
- return (_jsx(Slot, { ref: ref, id: formItemId, "aria-describedby": !error
44
- ? `${formDescriptionId}`
45
- : `${formDescriptionId} ${formMessageId}`, "aria-invalid": !!error, ...props }));
46
- });
47
- FormControl.displayName = "FormControl";
48
- const FormDescription = React.forwardRef(({ className, ...props }, ref) => {
49
- const { formDescriptionId } = useFormField();
50
- return (_jsx("p", { ref: ref, id: formDescriptionId, className: cn("text-sm text-muted-foreground", className), ...props }));
51
- });
52
- FormDescription.displayName = "FormDescription";
53
- const FormMessage = React.forwardRef(({ className, children, ...props }, ref) => {
54
- const { error, formMessageId } = useFormField();
55
- const body = error ? String(error?.message) : children;
56
- if (!body) {
57
- return null;
58
- }
59
- return (_jsx("p", { ref: ref, id: formMessageId, className: cn("text-sm font-medium text-destructive", className), ...props, children: body }));
60
- });
61
- FormMessage.displayName = "FormMessage";
62
- export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField, };
1
+ import{jsx as e}from"react/jsx-runtime";import*as r from"react";import{Slot as t}from"@radix-ui/react-slot";import{Controller as o,FormProvider as s,useFormContext as m}from"react-hook-form";import{cn as a}from"../../shared/lib/utils";import{Label as i}from"../../components/ui/label";const d=s,n=r.createContext({}),c=({...r})=>e(n.Provider,{value:{name:r.name},children:e(o,{...r})}),f=()=>{const e=r.useContext(n),t=r.useContext(l),{getFieldState:o,formState:s}=m(),a=o(e.name,s);if(!e)throw new Error("useFormField should be used within <FormField>");const{id:i}=t;return{id:i,name:e.name,formItemId:`${i}-form-item`,formDescriptionId:`${i}-form-item-description`,formMessageId:`${i}-form-item-message`,...a}},l=r.createContext({}),u=r.forwardRef(({className:t,...o},s)=>{const m=r.useId();return e(l.Provider,{value:{id:m},children:e("div",{ref:s,className:a("space-y-2",t),...o})})});u.displayName="FormItem";const p=r.forwardRef(({className:r,...t},o)=>{const{error:s,formItemId:m}=f();return e(i,{ref:o,className:a(s&&"text-destructive",r),htmlFor:m,...t})});p.displayName="FormLabel";const I=r.forwardRef(({...r},o)=>{const{error:s,formItemId:m,formDescriptionId:a,formMessageId:i}=f();return e(t,{ref:o,id:m,"aria-describedby":s?`${a} ${i}`:`${a}`,"aria-invalid":!!s,...r})});I.displayName="FormControl";const x=r.forwardRef(({className:r,...t},o)=>{const{formDescriptionId:s}=f();return e("p",{ref:o,id:s,className:a("text-sm text-muted-foreground",r),...t})});x.displayName="FormDescription";const N=r.forwardRef(({className:r,children:t,...o},s)=>{const{error:m,formMessageId:i}=f(),d=m?String(m?.message):t;return d?e("p",{ref:s,id:i,className:a("text-sm font-medium text-destructive",r),...o,children:d}):null});N.displayName="FormMessage";export{f as useFormField,d as Form,u as FormItem,p as FormLabel,I as FormControl,x as FormDescription,N as FormMessage,c as FormField};
@@ -1,10 +1 @@
1
- 'use client';
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import * as React from 'react';
4
- import * as HoverCardPrimitive from '@radix-ui/react-hover-card';
5
- import { cn } from '../../shared/lib/utils';
6
- const HoverCard = HoverCardPrimitive.Root;
7
- const HoverCardTrigger = HoverCardPrimitive.Trigger;
8
- const HoverCardContent = React.forwardRef(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (_jsx(HoverCardPrimitive.Content, { ref: ref, align: align, sideOffset: sideOffset, className: cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 rounded-md border p-4 shadow-md outline-none', className), ...props })));
9
- HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
10
- export { HoverCard, HoverCardTrigger, HoverCardContent };
1
+ "use client";import{jsx as t}from"react/jsx-runtime";import*as e from"react";import*as a from"@radix-ui/react-hover-card";import{cn as o}from"../../shared/lib/utils";const d=a.Root,i=a.Trigger,s=e.forwardRef(({className:e,align:d="center",sideOffset:i=4,...s},r)=>t(a.Content,{ref:r,align:d,sideOffset:i,className:o("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 rounded-md border p-4 shadow-md outline-none",e),...s}));s.displayName=a.Content.displayName;export{d as HoverCard,i as HoverCardTrigger,s as HoverCardContent};
@@ -1,29 +1 @@
1
- 'use client';
2
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useCallback, useEffect } from 'react';
4
- import { ImageOff } from 'lucide-react';
5
- import { cn } from '../../shared/lib/utils';
6
- /**
7
- * `ImagePreview` – a drop-in replacement for `<img>` that gracefully handles
8
- * broken / invalid image URLs by showing a friendly placeholder instead of the
9
- * browser's default broken-image icon.
10
- */
11
- export function ImagePreview({ src, alt = '', className, fallback, ...props }) {
12
- const [hasError, setHasError] = useState(false);
13
- // Reset error state when src changes so a new URL gets a fresh attempt
14
- useEffect(() => {
15
- setHasError(false);
16
- }, [src]);
17
- const handleError = useCallback(() => {
18
- setHasError(true);
19
- }, []);
20
- const handleLoad = useCallback(() => {
21
- setHasError(false);
22
- }, []);
23
- if (hasError || !src) {
24
- if (fallback)
25
- return _jsx(_Fragment, { children: fallback });
26
- return (_jsxs("div", { className: cn('flex flex-col items-center justify-center gap-2 bg-gray-50 text-gray-400', className), children: [_jsx(ImageOff, { className: "h-8 w-8" }), _jsx("span", { className: "text-xs font-medium", children: "Kh\u00F4ng th\u1EC3 t\u1EA3i \u1EA3nh" }), _jsx("span", { className: "max-w-[80%] truncate text-[10px] text-gray-300", title: src, children: src })] }));
27
- }
28
- return (_jsx("img", { src: src, alt: alt, className: cn('object-contain', className), onError: handleError, onLoad: handleLoad, ...props }));
29
- }
1
+ "use client";import{Fragment as e,jsx as t,jsxs as a}from"react/jsx-runtime";import{useState as r,useCallback as s,useEffect as c}from"react";import{ImageOff as n}from"lucide-react";import{cn as i}from"../../shared/lib/utils";export function ImagePreview({src:l,alt:m="",className:o,fallback:x,...f}){const[d,g]=r(!1);c(()=>{g(!1)},[l]);const h=s(()=>{g(!0)},[]),p=s(()=>{g(!1)},[]);return d||!l?x?t(e,{children:x}):a("div",{className:i("flex flex-col items-center justify-center gap-2 bg-gray-50 text-gray-400",o),children:[t(n,{className:"h-8 w-8"}),t("span",{className:"text-xs font-medium",children:"Không thể tải ảnh"}),t("span",{className:"max-w-[80%] truncate text-[10px] text-gray-300",title:l,children:l})]}):t("img",{src:l,alt:m,className:i("object-contain",o),onError:h,onLoad:p,...f})}
@@ -1,8 +1 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import * as React from 'react';
3
- import { cn } from '../../shared/lib/utils';
4
- const Input = React.forwardRef(({ className, type, ...props }, ref) => {
5
- return (_jsx("input", { type: type, className: cn('border-input bg-background file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/20 flex h-10 w-full rounded-md border px-3 py-2 text-base shadow-xs transition-[color,box-shadow] file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-[3px] focus-visible:ring-offset-0 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm', className), ref: ref, ...props }));
6
- });
7
- Input.displayName = 'Input';
8
- export { Input };
1
+ import{jsx as e}from"react/jsx-runtime";import*as r from"react";import{cn as o}from"../../shared/lib/utils";const i=r.forwardRef(({className:r,type:i,...s},t)=>e("input",{type:i,className:o("border-input bg-background file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/20 flex h-10 w-full rounded-md border px-3 py-2 text-base shadow-xs transition-[color,box-shadow] file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-[3px] focus-visible:ring-offset-0 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",r),ref:t,...s}));i.displayName="Input";export{i as Input};
@@ -1,9 +1 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import * as React from 'react';
3
- import * as LabelPrimitive from '@radix-ui/react-label';
4
- import { cva } from 'class-variance-authority';
5
- import { cn } from '../../shared/lib/utils';
6
- const labelVariants = cva('text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70');
7
- const Label = React.forwardRef(({ className, ...props }, ref) => (_jsx(LabelPrimitive.Root, { ref: ref, className: cn(labelVariants(), className), ...props })));
8
- Label.displayName = LabelPrimitive.Root.displayName;
9
- export { Label };
1
+ import{jsx as a}from"react/jsx-runtime";import*as r from"react";import*as e from"@radix-ui/react-label";import{cva as o}from"class-variance-authority";import{cn as t}from"../../shared/lib/utils";const s=o("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"),i=r.forwardRef(({className:r,...o},i)=>a(e.Root,{ref:i,className:t(s(),r),...o}));i.displayName=e.Root.displayName;export{i as Label};
@@ -1,15 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useLocaleStore } from '../../shared/lib/stores/localeStore';
4
- import { SUPPORTED_LOCALES } from '../../shared/lib/i18n';
5
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../components/ui/select';
6
- import { Globe } from 'lucide-react';
7
- const LOCALE_LABELS = {
8
- vi: 'Tiếng Việt',
9
- en: 'English',
10
- };
11
- export function LanguageSwitcher() {
12
- const locale = useLocaleStore((s) => s.locale);
13
- const setLocale = useLocaleStore((s) => s.setLocale);
14
- return (_jsxs(Select, { value: locale, onValueChange: (v) => setLocale(v), children: [_jsxs(SelectTrigger, { children: [_jsx(Globe, { className: "mr-2 h-4 w-4" }), _jsx(SelectValue, {})] }), _jsx(SelectContent, { children: SUPPORTED_LOCALES.map((l) => (_jsx(SelectItem, { value: l, children: LOCALE_LABELS[l] }, l))) })] }));
15
- }
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useLocaleStore as i}from"../../shared/lib/stores/localeStore";import{SUPPORTED_LOCALES as o}from"../../shared/lib/i18n";import{Select as t,SelectContent as n,SelectItem as l,SelectTrigger as s,SelectValue as a}from"../../components/ui/select";import{Globe as c}from"lucide-react";const m={vi:"Tiếng Việt",en:"English"};export function LanguageSwitcher(){const h=i(e=>e.locale),u=i(e=>e.setLocale);return r(t,{value:h,onValueChange:e=>u(e),children:[r(s,{children:[e(c,{className:"mr-2 h-4 w-4"}),e(a,{})]}),e(n,{children:o.map(r=>e(l,{value:r,children:m[r]},r))})]})}
@@ -1,90 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import * as React from 'react';
4
- import { X, ChevronDown, Check, Search } from 'lucide-react';
5
- import { Badge } from '../../components/ui/badge';
6
- import { Input } from '../../components/ui/input';
7
- import { cn } from '../../shared/lib/utils';
8
- const MAX_VISIBLE_BADGES = 5;
9
- export function MultiSelect({ options, selected, onChange, placeholder = 'Select items...', className, disabled = false, }) {
10
- const [open, setOpen] = React.useState(false);
11
- const [searchQuery, setSearchQuery] = React.useState('');
12
- const dropdownRef = React.useRef(null);
13
- const searchInputRef = React.useRef(null);
14
- // Close dropdown when clicking outside
15
- React.useEffect(() => {
16
- const handleClickOutside = (event) => {
17
- if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
18
- setOpen(false);
19
- }
20
- };
21
- if (open) {
22
- document.addEventListener('mousedown', handleClickOutside);
23
- }
24
- return () => {
25
- document.removeEventListener('mousedown', handleClickOutside);
26
- };
27
- }, [open]);
28
- // Focus search input when dropdown opens
29
- React.useEffect(() => {
30
- if (open && searchInputRef.current) {
31
- setTimeout(() => {
32
- searchInputRef.current?.focus();
33
- }, 0);
34
- }
35
- else {
36
- setSearchQuery('');
37
- }
38
- }, [open]);
39
- const handleToggle = (value) => {
40
- if (selected.includes(value)) {
41
- onChange(selected.filter((v) => v !== value));
42
- }
43
- else {
44
- onChange([...selected, value]);
45
- }
46
- };
47
- const handleRemove = (value) => {
48
- onChange(selected.filter((v) => v !== value));
49
- };
50
- const selectedOptions = options.filter((option) => selected.includes(option.value));
51
- // Filter options based on search query
52
- const filteredOptions = React.useMemo(() => {
53
- if (!searchQuery)
54
- return options;
55
- const query = searchQuery.toLowerCase();
56
- return options.filter((option) => option.label.toLowerCase().includes(query) ||
57
- option.description?.toLowerCase().includes(query));
58
- }, [options, searchQuery]);
59
- return (_jsxs("div", { ref: dropdownRef, className: cn('relative', className), children: [_jsxs("div", { role: "button", tabIndex: disabled ? -1 : 0, "aria-disabled": disabled, onClick: () => !disabled && setOpen(!open), onKeyDown: (e) => {
60
- if (e.key === 'Enter' || e.key === ' ') {
61
- e.preventDefault();
62
- if (!disabled) {
63
- setOpen(!open);
64
- }
65
- }
66
- }, className: cn('border-input bg-background placeholder:text-muted-foreground focus:border-ring focus:ring-ring/20 flex min-h-10 w-full cursor-pointer items-center justify-between rounded-md border px-3 py-2 text-sm shadow-xs transition-[color,box-shadow] focus:ring-[3px] focus:ring-offset-0 focus:outline-none', disabled && 'cursor-not-allowed opacity-50', !selected.length && 'text-muted-foreground'), children: [_jsx("div", { className: "flex flex-1 flex-wrap gap-1", children: selected.length === 0 ? (_jsx("span", { className: "text-muted-foreground", children: placeholder })) : (_jsxs(_Fragment, { children: [selectedOptions.slice(0, MAX_VISIBLE_BADGES).map((option) => (_jsxs(Badge, { variant: "secondary", className: "py-0.5 pr-1 pl-2", children: [_jsx("span", { className: "text-xs", children: option.label }), _jsx("span", { role: "button", tabIndex: 0, className: "ml-1 inline-flex cursor-pointer rounded-full outline-none focus:ring-ring/20 focus:ring-[3px] focus:ring-offset-0", onClick: (e) => {
67
- e.preventDefault();
68
- e.stopPropagation();
69
- handleRemove(option.value);
70
- }, onKeyDown: (e) => {
71
- if (e.key === 'Enter' || e.key === ' ') {
72
- e.preventDefault();
73
- e.stopPropagation();
74
- handleRemove(option.value);
75
- }
76
- }, children: _jsx(X, { className: "text-muted-foreground hover:text-foreground h-3 w-3" }) })] }, option.value))), selectedOptions.length > MAX_VISIBLE_BADGES && (_jsx(Badge, { variant: "secondary", className: "py-0.5 px-2", children: _jsxs("span", { className: "text-xs", children: ["+", selectedOptions.length - MAX_VISIBLE_BADGES] }) }))] })) }), _jsx(ChevronDown, { className: "ml-2 h-4 w-4 shrink-0 opacity-50" })] }), open && (_jsxs("div", { className: "bg-popover animate-in fade-in-0 zoom-in-95 absolute z-50 mt-2 w-full rounded-md border shadow-md", children: [_jsx("div", { className: "border-b p-2", children: _jsxs("div", { className: "relative", children: [_jsx(Search, { className: "text-muted-foreground absolute top-1/2 left-2 h-4 w-4 -translate-y-1/2" }), _jsx(Input, { ref: searchInputRef, type: "text", placeholder: "T\u00ECm ki\u1EBFm...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "h-9 pl-8", onClick: (e) => e.stopPropagation() })] }) }), selected.length > 0 && (_jsx("div", { className: "border-b bg-gray-50 p-2", children: _jsxs("div", { className: "flex flex-wrap gap-1", children: [selectedOptions.slice(0, MAX_VISIBLE_BADGES).map((option) => (_jsxs(Badge, { variant: "secondary", className: "py-1 pr-1 pl-2", children: [_jsx("span", { className: "text-xs", children: option.label }), _jsx("span", { role: "button", tabIndex: 0, className: "ml-1 inline-flex cursor-pointer rounded-full outline-none focus:ring-ring/20 focus:ring-[3px] focus:ring-offset-0", onClick: (e) => {
77
- e.preventDefault();
78
- e.stopPropagation();
79
- handleRemove(option.value);
80
- }, onKeyDown: (e) => {
81
- if (e.key === 'Enter' || e.key === ' ') {
82
- e.preventDefault();
83
- e.stopPropagation();
84
- handleRemove(option.value);
85
- }
86
- }, children: _jsx(X, { className: "text-muted-foreground hover:text-foreground h-3 w-3" }) })] }, option.value))), selectedOptions.length > MAX_VISIBLE_BADGES && (_jsx(Badge, { variant: "secondary", className: "py-1 px-2", children: _jsxs("span", { className: "text-xs", children: ["+", selectedOptions.length - MAX_VISIBLE_BADGES, " m\u1EE5c kh\u00E1c"] }) }))] }) })), _jsx("div", { className: "max-h-[260px] overflow-auto p-1", children: filteredOptions.length === 0 ? (_jsx("div", { className: "text-muted-foreground py-6 text-center text-sm", children: searchQuery ? 'Không tìm thấy kết quả' : 'No options available' })) : (filteredOptions.map((option) => {
87
- const isSelected = selected.includes(option.value);
88
- return (_jsxs("div", { className: cn('hover:bg-accent hover:text-accent-foreground relative flex cursor-pointer items-center rounded-sm px-2 py-2 text-sm transition-colors outline-none select-none', isSelected && 'bg-accent'), onClick: () => handleToggle(option.value), children: [_jsxs("div", { className: "flex flex-1 items-center gap-2", children: [_jsx("span", { "aria-hidden": "true", "data-state": isSelected ? 'checked' : 'unchecked', className: cn('border-input size-4 shrink-0 rounded-[4px] border shadow-xs', 'flex items-center justify-center transition-colors', isSelected && 'border-primary bg-primary text-primary-foreground'), children: isSelected && _jsx(Check, { className: "size-3.5" }) }), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { children: option.label }), option.description && (_jsx("span", { className: "text-muted-foreground text-xs", children: option.description }))] })] }), isSelected && _jsx(Check, { className: "ml-auto h-4 w-4" })] }, option.value));
89
- })) })] }))] }));
90
- }
1
+ "use client";import{jsx as e,jsxs as r,Fragment as t}from"react/jsx-runtime";import*as n from"react";import{X as a,ChevronDown as o,Check as s,Search as l}from"lucide-react";import{Badge as i}from"../../components/ui/badge";import{Input as c}from"../../components/ui/input";import{cn as d}from"../../shared/lib/utils";export function MultiSelect({options:u,selected:m,onChange:p,placeholder:f="Select items...",className:h,disabled:x=!1}){const[g,v]=n.useState(!1),[b,N]=n.useState(""),y=n.useRef(null),w=n.useRef(null);n.useEffect(()=>{const e=e=>{y.current&&!y.current.contains(e.target)&&v(!1)};return g&&document.addEventListener("mousedown",e),()=>{document.removeEventListener("mousedown",e)}},[g]),n.useEffect(()=>{g&&w.current?setTimeout(()=>{w.current?.focus()},0):N("")},[g]);const k=e=>{p(m.filter(r=>r!==e))},C=u.filter(e=>m.includes(e.value)),D=n.useMemo(()=>{if(!b)return u;const e=b.toLowerCase();return u.filter(r=>r.label.toLowerCase().includes(e)||r.description?.toLowerCase().includes(e))},[u,b]);return r("div",{ref:y,className:d("relative",h),children:[r("div",{role:"button",tabIndex:x?-1:0,"aria-disabled":x,onClick:()=>!x&&v(!g),onKeyDown:e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),x||v(!g))},className:d("border-input bg-background placeholder:text-muted-foreground focus:border-ring focus:ring-ring/20 flex min-h-10 w-full cursor-pointer items-center justify-between rounded-md border px-3 py-2 text-sm shadow-xs transition-[color,box-shadow] focus:ring-[3px] focus:ring-offset-0 focus:outline-none",x&&"cursor-not-allowed opacity-50",!m.length&&"text-muted-foreground"),children:[e("div",{className:"flex flex-1 flex-wrap gap-1",children:0===m.length?e("span",{className:"text-muted-foreground",children:f}):r(t,{children:[C.slice(0,5).map(t=>r(i,{variant:"secondary",className:"py-0.5 pr-1 pl-2",children:[e("span",{className:"text-xs",children:t.label}),e("span",{role:"button",tabIndex:0,className:"ml-1 inline-flex cursor-pointer rounded-full outline-none focus:ring-ring/20 focus:ring-[3px] focus:ring-offset-0",onClick:e=>{e.preventDefault(),e.stopPropagation(),k(t.value)},onKeyDown:e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),e.stopPropagation(),k(t.value))},children:e(a,{className:"text-muted-foreground hover:text-foreground h-3 w-3"})})]},t.value)),C.length>5&&e(i,{variant:"secondary",className:"py-0.5 px-2",children:r("span",{className:"text-xs",children:["+",C.length-5]})})]})}),e(o,{className:"ml-2 h-4 w-4 shrink-0 opacity-50"})]}),g&&r("div",{className:"bg-popover animate-in fade-in-0 zoom-in-95 absolute z-50 mt-2 w-full rounded-md border shadow-md",children:[e("div",{className:"border-b p-2",children:r("div",{className:"relative",children:[e(l,{className:"text-muted-foreground absolute top-1/2 left-2 h-4 w-4 -translate-y-1/2"}),e(c,{ref:w,type:"text",placeholder:"Tìm kiếm...",value:b,onChange:e=>N(e.target.value),className:"h-9 pl-8",onClick:e=>e.stopPropagation()})]})}),m.length>0&&e("div",{className:"border-b bg-gray-50 p-2",children:r("div",{className:"flex flex-wrap gap-1",children:[C.slice(0,5).map(t=>r(i,{variant:"secondary",className:"py-1 pr-1 pl-2",children:[e("span",{className:"text-xs",children:t.label}),e("span",{role:"button",tabIndex:0,className:"ml-1 inline-flex cursor-pointer rounded-full outline-none focus:ring-ring/20 focus:ring-[3px] focus:ring-offset-0",onClick:e=>{e.preventDefault(),e.stopPropagation(),k(t.value)},onKeyDown:e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),e.stopPropagation(),k(t.value))},children:e(a,{className:"text-muted-foreground hover:text-foreground h-3 w-3"})})]},t.value)),C.length>5&&e(i,{variant:"secondary",className:"py-1 px-2",children:r("span",{className:"text-xs",children:["+",C.length-5," mục khác"]})})]})}),e("div",{className:"max-h-[260px] overflow-auto p-1",children:0===D.length?e("div",{className:"text-muted-foreground py-6 text-center text-sm",children:b?"Không tìm thấy kết quả":"No options available"}):D.map(t=>{const n=m.includes(t.value);return r("div",{className:d("hover:bg-accent hover:text-accent-foreground relative flex cursor-pointer items-center rounded-sm px-2 py-2 text-sm transition-colors outline-none select-none",n&&"bg-accent"),onClick:()=>{return e=t.value,void(m.includes(e)?p(m.filter(r=>r!==e)):p([...m,e]));var e},children:[r("div",{className:"flex flex-1 items-center gap-2",children:[e("span",{"aria-hidden":"true","data-state":n?"checked":"unchecked",className:d("border-input size-4 shrink-0 rounded-[4px] border shadow-xs","flex items-center justify-center transition-colors",n&&"border-primary bg-primary text-primary-foreground"),children:n&&e(s,{className:"size-3.5"})}),r("div",{className:"flex flex-col",children:[e("span",{children:t.label}),t.description&&e("span",{className:"text-muted-foreground text-xs",children:t.description})]})]}),n&&e(s,{className:"ml-auto h-4 w-4"})]},t.value)})})]})]})}
@@ -1,33 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { forwardRef } from 'react';
4
- import { Input } from '../../components/ui/input';
5
- import { Label } from '../../components/ui/label';
6
- import { cn } from '../../shared/lib/utils';
7
- /**
8
- * A specialized input component for "Điểm số" (points/score) fields.
9
- * - Only accepts positive integers
10
- * - Allows clearing the input to enter new values
11
- * - Strips non-digit characters automatically
12
- */
13
- export const PointsInput = forwardRef(({ label = 'Điểm số', error, required = true, allowZero = false, className, id, ...props }, ref) => {
14
- const inputId = id || 'points';
15
- const handleInput = (e) => {
16
- const input = e.target;
17
- // Allow empty value for clearing
18
- if (input.value === '')
19
- return;
20
- // Remove any non-digit characters and parse as integer
21
- const value = input.value.replace(/[^0-9]/g, '');
22
- if (value === '') {
23
- input.value = '';
24
- }
25
- else {
26
- const intValue = parseInt(value, 10);
27
- const minVal = allowZero ? 0 : 1;
28
- input.value = intValue < minVal ? minVal.toString() : intValue.toString();
29
- }
30
- };
31
- return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Label, { htmlFor: inputId, className: "whitespace-nowrap text-sm font-medium", children: [label, " ", required && _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { ref: ref, id: inputId, type: "number", step: "1", min: allowZero ? "0" : "1", onInput: handleInput, className: cn('w-20', error && 'border-red-500 focus:ring-red-500', className), ...props }), error && _jsx("p", { className: "text-sm text-red-600", children: error })] }));
32
- });
33
- PointsInput.displayName = 'PointsInput';
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{forwardRef as r}from"react";import{Input as s}from"../../components/ui/input";import{Label as n}from"../../components/ui/label";import{cn as i}from"../../shared/lib/utils";export const PointsInput=r(({label:r="Điểm số",error:o,required:a=!0,allowZero:m=!1,className:l,id:c,...p},u)=>{const d=c||"points";return t("div",{className:"flex items-center gap-2",children:[t(n,{htmlFor:d,className:"whitespace-nowrap text-sm font-medium",children:[r," ",a&&e("span",{className:"text-red-500",children:"*"})]}),e(s,{ref:u,id:d,type:"number",step:"1",min:m?"0":"1",onInput:e=>{const t=e.target;if(""===t.value)return;const r=t.value.replace(/[^0-9]/g,"");if(""===r)t.value="";else{const e=parseInt(r,10),s=m?0:1;t.value=e<s?s.toString():e.toString()}},className:i("w-20",o&&"border-red-500 focus:ring-red-500",l),...p}),o&&e("p",{className:"text-sm text-red-600",children:o})]})});PointsInput.displayName="PointsInput";
@@ -1,26 +1 @@
1
- "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import { Popover as PopoverPrimitive } from "radix-ui";
4
- import { cn } from "../../shared/lib/utils";
5
- function Popover({ ...props }) {
6
- return _jsx(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
7
- }
8
- function PopoverTrigger({ ...props }) {
9
- return _jsx(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
10
- }
11
- function PopoverContent({ className, align = "center", sideOffset = 4, ...props }) {
12
- return (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { "data-slot": "popover-content", align: align, sideOffset: sideOffset, className: cn("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden", className), ...props }) }));
13
- }
14
- function PopoverAnchor({ ...props }) {
15
- return _jsx(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
16
- }
17
- function PopoverHeader({ className, ...props }) {
18
- return (_jsx("div", { "data-slot": "popover-header", className: cn("flex flex-col gap-1 text-sm", className), ...props }));
19
- }
20
- function PopoverTitle({ className, ...props }) {
21
- return (_jsx("div", { "data-slot": "popover-title", className: cn("font-medium", className), ...props }));
22
- }
23
- function PopoverDescription({ className, ...props }) {
24
- return (_jsx("p", { "data-slot": "popover-description", className: cn("text-muted-foreground", className), ...props }));
25
- }
26
- export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor, PopoverHeader, PopoverTitle, PopoverDescription, };
1
+ "use client";import{jsx as t}from"react/jsx-runtime";import{Popover as o}from"radix-ui";import{cn as e}from"../../shared/lib/utils";function a({...e}){return t(o.Root,{"data-slot":"popover",...e})}function r({...e}){return t(o.Trigger,{"data-slot":"popover-trigger",...e})}function n({className:a,align:r="center",sideOffset:n=4,...i}){return t(o.Portal,{children:t(o.Content,{"data-slot":"popover-content",align:r,sideOffset:n,className:e("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",a),...i})})}function i({...e}){return t(o.Anchor,{"data-slot":"popover-anchor",...e})}function s({className:o,...a}){return t("div",{"data-slot":"popover-header",className:e("flex flex-col gap-1 text-sm",o),...a})}function d({className:o,...a}){return t("div",{"data-slot":"popover-title",className:e("font-medium",o),...a})}function l({className:o,...a}){return t("p",{"data-slot":"popover-description",className:e("text-muted-foreground",o),...a})}export{a as Popover,r as PopoverTrigger,n as PopoverContent,i as PopoverAnchor,s as PopoverHeader,d as PopoverTitle,l as PopoverDescription};
@@ -1,16 +1 @@
1
- 'use client';
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import * as React from 'react';
4
- import * as ProgressPrimitive from '@radix-ui/react-progress';
5
- import { cn } from '../../shared/lib/utils';
6
- const Progress = React.forwardRef(({ className, value, ...props }, ref) => (_jsx(ProgressPrimitive.Root, { ref: ref, className: cn('relative h-2 w-full overflow-hidden rounded-full bg-gray-200', className), ...props, children: _jsx(ProgressPrimitive.Indicator, { className: cn('h-full w-full flex-1 rounded-full transition-all',
7
- // Mantis style - Green progress bar
8
- value && value >= 80
9
- ? 'bg-green-500'
10
- : value && value >= 50
11
- ? 'bg-blue-500'
12
- : value && value >= 30
13
- ? 'bg-orange-500'
14
- : 'bg-red-500'), style: { transform: `translateX(-${100 - (value || 0)}%)` } }) })));
15
- Progress.displayName = ProgressPrimitive.Root.displayName;
16
- export { Progress };
1
+ "use client";import{jsx as r}from"react/jsx-runtime";import*as e from"react";import*as a from"@radix-ui/react-progress";import{cn as l}from"../../shared/lib/utils";const o=e.forwardRef(({className:e,value:o,...s},t)=>r(a.Root,{ref:t,className:l("relative h-2 w-full overflow-hidden rounded-full bg-gray-200",e),...s,children:r(a.Indicator,{className:l("h-full w-full flex-1 rounded-full transition-all",o&&o>=80?"bg-green-500":o&&o>=50?"bg-blue-500":o&&o>=30?"bg-orange-500":"bg-red-500"),style:{transform:`translateX(-${100-(o||0)}%)`}})}));o.displayName=a.Root.displayName;export{o as Progress};
@@ -1,12 +1 @@
1
- 'use client';
2
- import { jsx as _jsx } from "react/jsx-runtime";
3
- import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
4
- import { CircleIcon } from 'lucide-react';
5
- import { cn } from '../../shared/lib/utils';
6
- function RadioGroup({ className, ...props }) {
7
- return (_jsx(RadioGroupPrimitive.Root, { "data-slot": "radio-group", className: cn('grid gap-3', className), ...props }));
8
- }
9
- function RadioGroupItem({ className, ...props }) {
10
- return (_jsx(RadioGroupPrimitive.Item, { "data-slot": "radio-group-item", className: cn('border-input text-primary focus-visible:border-ring focus-visible:ring-ring/20 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50', className), ...props, children: _jsx(RadioGroupPrimitive.Indicator, { "data-slot": "radio-group-indicator", className: "relative flex items-center justify-center", children: _jsx(CircleIcon, { className: "fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2" }) }) }));
11
- }
12
- export { RadioGroup, RadioGroupItem };
1
+ "use client";import{jsx as r}from"react/jsx-runtime";import*as i from"@radix-ui/react-radio-group";import{CircleIcon as a}from"lucide-react";import{cn as e}from"../../shared/lib/utils";function t({className:a,...t}){return r(i.Root,{"data-slot":"radio-group",className:e("grid gap-3",a),...t})}function s({className:t,...s}){return r(i.Item,{"data-slot":"radio-group-item",className:e("border-input text-primary focus-visible:border-ring focus-visible:ring-ring/20 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50",t),...s,children:r(i.Indicator,{"data-slot":"radio-group-indicator",className:"relative flex items-center justify-center",children:r(a,{className:"fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2"})})})}export{t as RadioGroup,s as RadioGroupItem};
@@ -1,11 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
4
- import { cn } from '../../shared/lib/utils';
5
- function ScrollArea({ className, children, ...props }) {
6
- return (_jsxs(ScrollAreaPrimitive.Root, { "data-slot": "scroll-area", className: cn('relative', className), ...props, children: [_jsx(ScrollAreaPrimitive.Viewport, { "data-slot": "scroll-area-viewport", className: "focus-visible:ring-ring/20 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-offset-0 focus-visible:outline-1", children: children }), _jsx(ScrollBar, {}), _jsx(ScrollAreaPrimitive.Corner, {})] }));
7
- }
8
- function ScrollBar({ className, orientation = 'vertical', ...props }) {
9
- return (_jsx(ScrollAreaPrimitive.ScrollAreaScrollbar, { "data-slot": "scroll-area-scrollbar", orientation: orientation, className: cn('flex touch-none p-px transition-colors select-none', orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent', orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent', className), ...props, children: _jsx(ScrollAreaPrimitive.ScrollAreaThumb, { "data-slot": "scroll-area-thumb", className: "bg-border relative flex-1 rounded-full" }) }));
10
- }
11
- export { ScrollArea, ScrollBar };
1
+ "use client";import{jsx as r,jsxs as e}from"react/jsx-runtime";import*as l from"@radix-ui/react-scroll-area";import{cn as o}from"../../shared/lib/utils";function a({className:a,children:s,...i}){return e(l.Root,{"data-slot":"scroll-area",className:o("relative",a),...i,children:[r(l.Viewport,{"data-slot":"scroll-area-viewport",className:"focus-visible:ring-ring/20 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-offset-0 focus-visible:outline-1",children:s}),r(t,{}),r(l.Corner,{})]})}function t({className:e,orientation:a="vertical",...t}){return r(l.ScrollAreaScrollbar,{"data-slot":"scroll-area-scrollbar",orientation:a,className:o("flex touch-none p-px transition-colors select-none","vertical"===a&&"h-full w-2.5 border-l border-l-transparent","horizontal"===a&&"h-2.5 flex-col border-t border-t-transparent",e),...t,children:r(l.ScrollAreaThumb,{"data-slot":"scroll-area-thumb",className:"bg-border relative flex-1 rounded-full"})})}export{a as ScrollArea,t as ScrollBar};
@@ -1,38 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import * as SelectPrimitive from '@radix-ui/react-select';
4
- import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from 'lucide-react';
5
- import { cn } from '../../shared/lib/utils';
6
- function Select({ ...props }) {
7
- return _jsx(SelectPrimitive.Root, { "data-slot": "select", ...props });
8
- }
9
- function SelectGroup({ ...props }) {
10
- return _jsx(SelectPrimitive.Group, { "data-slot": "select-group", ...props });
11
- }
12
- function SelectValue({ ...props }) {
13
- return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
14
- }
15
- function SelectTrigger({ className, size = 'default', children, ...props }) {
16
- return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn("border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/20 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDownIcon, { className: "size-4 opacity-50" }) })] }));
17
- }
18
- function SelectContent({ className, children, position = 'popper', align = 'center', ...props }) {
19
- return (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { "data-slot": "select-content", className: cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md', position === 'popper' &&
20
- 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', className), position: position, align: align, ...props, children: [_jsx(SelectScrollUpButton, {}), _jsx(SelectPrimitive.Viewport, { className: cn('p-1', position === 'popper' &&
21
- 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1'), children: children }), _jsx(SelectScrollDownButton, {})] }) }));
22
- }
23
- function SelectLabel({ className, ...props }) {
24
- return (_jsx(SelectPrimitive.Label, { "data-slot": "select-label", className: cn('text-muted-foreground px-2 py-1.5 text-xs', className), ...props }));
25
- }
26
- function SelectItem({ className, children, ...props }) {
27
- return (_jsxs(SelectPrimitive.Item, { "data-slot": "select-item", className: cn("focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className), ...props, children: [_jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(CheckIcon, { className: "size-4" }) }) }), _jsx(SelectPrimitive.ItemText, { children: children })] }));
28
- }
29
- function SelectSeparator({ className, ...props }) {
30
- return (_jsx(SelectPrimitive.Separator, { "data-slot": "select-separator", className: cn('bg-border pointer-events-none -mx-1 my-1 h-px', className), ...props }));
31
- }
32
- function SelectScrollUpButton({ className, ...props }) {
33
- return (_jsx(SelectPrimitive.ScrollUpButton, { "data-slot": "select-scroll-up-button", className: cn('flex cursor-default items-center justify-center py-1', className), ...props, children: _jsx(ChevronUpIcon, { className: "size-4" }) }));
34
- }
35
- function SelectScrollDownButton({ className, ...props }) {
36
- return (_jsx(SelectPrimitive.ScrollDownButton, { "data-slot": "select-scroll-down-button", className: cn('flex cursor-default items-center justify-center py-1', className), ...props, children: _jsx(ChevronDownIcon, { className: "size-4" }) }));
37
- }
38
- export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, };
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as a from"@radix-ui/react-select";import{CheckIcon as s,ChevronDownIcon as r,ChevronUpIcon as o}from"lucide-react";import{cn as i}from"../../shared/lib/utils";function n({...t}){return e(a.Root,{"data-slot":"select",...t})}function l({...t}){return e(a.Group,{"data-slot":"select-group",...t})}function d({...t}){return e(a.Value,{"data-slot":"select-value",...t})}function c({className:s,size:o="default",children:n,...l}){return t(a.Trigger,{"data-slot":"select-trigger","data-size":o,className:i("border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/20 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",s),...l,children:[n,e(a.Icon,{asChild:!0,children:e(r,{className:"size-4 opacity-50"})})]})}function u({className:s,children:r,position:o="popper",align:n="center",...l}){return e(a.Portal,{children:t(a.Content,{"data-slot":"select-content",className:i("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md","popper"===o&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",s),position:o,align:n,...l,children:[e(g,{}),e(a.Viewport,{className:i("p-1","popper"===o&&"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"),children:r}),e(h,{})]})})}function m({className:t,...s}){return e(a.Label,{"data-slot":"select-label",className:i("text-muted-foreground px-2 py-1.5 text-xs",t),...s})}function p({className:r,children:o,...n}){return t(a.Item,{"data-slot":"select-item",className:i("focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",r),...n,children:[e("span",{className:"absolute right-2 flex size-3.5 items-center justify-center",children:e(a.ItemIndicator,{children:e(s,{className:"size-4"})})}),e(a.ItemText,{children:o})]})}function f({className:t,...s}){return e(a.Separator,{"data-slot":"select-separator",className:i("bg-border pointer-events-none -mx-1 my-1 h-px",t),...s})}function g({className:t,...s}){return e(a.ScrollUpButton,{"data-slot":"select-scroll-up-button",className:i("flex cursor-default items-center justify-center py-1",t),...s,children:e(o,{className:"size-4"})})}function h({className:t,...s}){return e(a.ScrollDownButton,{"data-slot":"select-scroll-down-button",className:i("flex cursor-default items-center justify-center py-1",t),...s,children:e(r,{className:"size-4"})})}export{n as Select,u as SelectContent,l as SelectGroup,p as SelectItem,m as SelectLabel,h as SelectScrollDownButton,g as SelectScrollUpButton,f as SelectSeparator,c as SelectTrigger,d as SelectValue};