@tinyweb_dev/oe-exam-sdk 0.1.27 → 0.1.28

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 (44) hide show
  1. package/README.md +75 -2
  2. package/dist/components/questions/_shared/config/question-types.config.js +1 -1
  3. package/dist/components/questions/_shared/types/word-fill-structured-form.type.d.ts +1 -1
  4. package/dist/components/questions/types/choose-the-correct-answer-group/index.d.ts +1 -0
  5. package/dist/components/questions/types/choose-the-correct-answer-group/index.js +1 -1
  6. package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.d.ts +4 -0
  7. package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.js +1 -0
  8. package/dist/components/questions/types/choose-the-correct-answer-group/register.js +1 -1
  9. package/dist/components/questions/types/cross-out-word-group/index.d.ts +1 -0
  10. package/dist/components/questions/types/cross-out-word-group/index.js +1 -1
  11. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.d.ts +6 -0
  12. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.js +1 -0
  13. package/dist/components/questions/types/cross-out-word-group/register.js +1 -1
  14. package/dist/components/questions/types/true-false-group/index.d.ts +1 -0
  15. package/dist/components/questions/types/true-false-group/index.js +1 -1
  16. package/dist/components/questions/types/true-false-group/map-true-false-group-data.d.ts +4 -0
  17. package/dist/components/questions/types/true-false-group/map-true-false-group-data.js +1 -0
  18. package/dist/components/questions/types/true-false-group/register.js +1 -1
  19. package/dist/components/questions/types/word-fill-structured-form/index.d.ts +1 -0
  20. package/dist/components/questions/types/word-fill-structured-form/index.js +1 -1
  21. package/dist/components/questions/types/word-fill-structured-form/map-wfsf-question-data.d.ts +7 -0
  22. package/dist/components/questions/types/word-fill-structured-form/map-wfsf-question-data.js +1 -0
  23. package/dist/components/questions/types/word-fill-structured-form/register.js +1 -1
  24. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -1
  25. package/dist/components/questions/viewer/ChooseTheCorrectAnswerGroupViewer.d.ts +8 -0
  26. package/dist/components/questions/viewer/ChooseTheCorrectAnswerGroupViewer.js +1 -0
  27. package/dist/components/questions/viewer/CrossOutWordGroupViewer.d.ts +7 -0
  28. package/dist/components/questions/viewer/CrossOutWordGroupViewer.js +1 -0
  29. package/dist/components/questions/viewer/QuestionViewer.js +1 -1
  30. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.d.ts +4 -0
  31. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +1 -0
  32. package/dist/components/questions/viewer/TrueFalseGroupViewer.d.ts +8 -0
  33. package/dist/components/questions/viewer/TrueFalseGroupViewer.js +1 -0
  34. package/dist/components/questions/viewer/WordFillStructuredFormViewer.d.ts +9 -0
  35. package/dist/components/questions/viewer/WordFillStructuredFormViewer.js +1 -0
  36. package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.d.ts +4 -0
  37. package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.js +1 -0
  38. package/dist/components/questions/viewer/index.d.ts +6 -0
  39. package/dist/components/questions/viewer/index.js +1 -1
  40. package/dist/index.d.ts +0 -1
  41. package/dist/index.js +1 -1
  42. package/dist/shared/constants/question-skills.js +1 -1
  43. package/dist/shared/types/common.types.d.ts +1 -1
  44. package/package.json +3 -2
package/README.md CHANGED
@@ -88,10 +88,10 @@ export function ResultReviewPage({ attemptId, token, router }) {
88
88
  ## Entry points
89
89
 
90
90
  ```txt
91
- @tinyweb_dev/oe-exam-sdk # export tổng hợp thường dùng
91
+ @tinyweb_dev/oe-exam-sdk # export tổng hợp browser-safe (UI + API)
92
92
  @tinyweb_dev/oe-exam-sdk/api # API adapters, constants, types
93
93
  @tinyweb_dev/oe-exam-sdk/shared # shared constants/types/helpers
94
- @tinyweb_dev/oe-exam-sdk/mcp # MCP server entry tools
94
+ @tinyweb_dev/oe-exam-sdk/mcp # MCP server ONLY (Node/stdio) — không re-export từ root
95
95
  @tinyweb_dev/oe-exam-sdk/components # shared UI components
96
96
  @tinyweb_dev/oe-exam-sdk/components/exams # màn tạo đề / câu hỏi / làm bài / vào phòng
97
97
  @tinyweb_dev/oe-exam-sdk/components/exams/entry # màn vào phòng (entry) + state machine
@@ -101,6 +101,8 @@ export function ResultReviewPage({ attemptId, token, router }) {
101
101
  @tinyweb_dev/oe-exam-sdk/components/results # màn xem chi tiết kết quả
102
102
  ```
103
103
 
104
+ > **MCP:** import từ subpath `@tinyweb_dev/oe-exam-sdk/mcp` (CLI/bin `oe-exam-mcp`). Root barrel **không** export MCP để tránh kéo `node:fs` vào client Next.js (`UnhandledSchemeError`).
105
+
104
106
  ## API adapters
105
107
 
106
108
  SDK không fix cứng API client. Host app có thể truyền adapter riêng qua prop `api`, hoặc dùng adapter mặc định:
@@ -329,12 +331,83 @@ import {
329
331
  - Các tích hợp host-system nên đi qua props/API adapter.
330
332
  - Upload file trong component mặc định dùng `blob:` URL cục bộ; API upload thật nên truyền từ host app khi cần.
331
333
 
334
+ ## Dev local với host Next.js (không build tay mỗi lần sửa)
335
+
336
+ SDK **không** export thẳng `src/` vì source dùng path alias `@/` / `@oe/exam` (được `tsc-alias` rewrite lúc build). Host app luôn resolve `exports` → `dist/`.
337
+
338
+ Để sửa SDK và host nhận ngay:
339
+
340
+ ### 1) Link package vào host
341
+
342
+ **Yarn (oe-school/frontend — đã cấu hình):**
343
+
344
+ ```json
345
+ // frontend/package.json
346
+ "@tinyweb_dev/oe-exam-sdk": "portal:../../oe-exam/sdk"
347
+ ```
348
+
349
+ ```bash
350
+ cd oe-school/frontend
351
+ yarn install
352
+ ```
353
+
354
+ **npm (host khác):**
355
+
356
+ ```bash
357
+ cd oe-exam/sdk && npm link
358
+ cd /path/to/host-nextjs && npm link @tinyweb_dev/oe-exam-sdk
359
+ ```
360
+
361
+ ### 2) Transpile package trong host Next.js
362
+
363
+ ```ts
364
+ // next.config.ts
365
+ const nextConfig = {
366
+ transpilePackages: ['@tinyweb_dev/oe-exam-sdk'],
367
+ };
368
+ ```
369
+
370
+ (`oe-school/frontend` đã có sẵn trong `transpilePackages`.)
371
+
372
+ ### 3) Chạy watch build SDK (bắt buộc — cập nhật `dist/`)
373
+
374
+ Terminal 1 — SDK:
375
+
376
+ ```bash
377
+ cd oe-exam/sdk
378
+ npm run dev
379
+ # initial build:ts (không minify) + tsc --watch
380
+ # → tsc-alias + check-aliases sau mỗi lần emit thành công + copy assets
381
+ ```
382
+
383
+ Terminal 2 — host:
384
+
385
+ ```bash
386
+ cd oe-school/frontend # hoặc host Next.js khác
387
+ yarn dev # / npm run dev
388
+ ```
389
+
390
+ Luồng:
391
+
392
+ ```txt
393
+ SDK src/** → npm run dev (watch) → dist/**
394
+ ↑ portal/npm link symlink
395
+ host node_modules/@tinyweb_dev/oe-exam-sdk
396
+ → Next transpilePackages + HMR
397
+ ```
398
+
399
+ Không cần: `npm pack` / publish / reinstall version mới mỗi lần sửa.
400
+
401
+ > Publish production vẫn dùng `npm run build` (có minify). `npm run dev` chỉ cho local.
402
+
332
403
  ## Kiểm tra local
333
404
 
334
405
  Trong repo này:
335
406
 
336
407
  ```bash
337
408
  npm --prefix sdk run build
409
+ # hoặc watch khi dev host:
410
+ npm --prefix sdk run dev
338
411
  ```
339
412
 
340
413
  Nếu chạy app dev:
@@ -1 +1 @@
1
- export const QUESTION_TYPE_CONFIG={FILL_IN_BLANK:{label:"Điền vào chỗ trống",requiresContent:!0,hasDedicatedComponent:!0},TRUE_FALSE:{label:"Đúng/Sai",requiresContent:!0,hasDedicatedComponent:!0},MATCHING:{label:"Nối",requiresContent:!0,hasDedicatedComponent:!1},ORDERING:{label:"Sắp xếp",requiresContent:!0,hasDedicatedComponent:!1},LISTENING:{label:"Nghe",requiresContent:!0,hasDedicatedComponent:!1},COLORING:{label:"Tô màu",requiresContent:!0,hasDedicatedComponent:!1},ESSAY:{label:"Viết luận",requiresContent:!0,hasDedicatedComponent:!1},LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE:{label:"Nghe - Điền chỗ trống",requiresContent:!0,hasDedicatedComponent:!1},LISTEN_AND_TICK_ANSWER:{label:"Nghe - Tick đáp án",requiresContent:!0,hasDedicatedComponent:!1},LISTEN_AND_FILL_NAME_NUMBER:{label:"Nghe - Điền tên/số",requiresContent:!0,hasDedicatedComponent:!1},TICK_TRUE_OR_FALSE:{label:"Đọc - Tick/Cross",requiresContent:!0,hasDedicatedComponent:!1},TICK_YES_OR_NO:{label:"Đọc - Tick/Cross (Yes/No)",requiresContent:!0,hasDedicatedComponent:!1},READING_TICK_CROSS:{label:"Đọc - Tick/Cross (Reading)",requiresContent:!0,hasDedicatedComponent:!1},ARRANGE_LETTERS_INTO_WORDS:{label:"Đọc - Sắp xếp chữ cái",requiresContent:!0,hasDedicatedComponent:!0},READ_PASSAGE_AND_COMPLETE_STORY:{label:"Đọc - Điền vào đoạn văn",requiresContent:!0,hasDedicatedComponent:!1},READ_PASSAGE_AND_ANSWER_QUESTIONS:{label:"Đọc đoạn văn và trả lời câu hỏi",requiresContent:!0,hasDedicatedComponent:!0},CLOZE_TEST_WITH_TICK_BOX:{label:"Cloze test với tick box",requiresContent:!0,hasDedicatedComponent:!1},READ_AND_CHOOSE_APPROPRIATE_WORD:{label:"Đọc - Chọn từ phù hợp",requiresContent:!0,hasDedicatedComponent:!1},WRITE_SHORT_PARAGRAPH:{label:"Viết - Đoạn văn ngắn",requiresContent:!0,hasDedicatedComponent:!0},FILL_MISSING_WORDS_IN_GRID:{label:"Điền từ còn thiếu vào grid",requiresContent:!0,hasDedicatedComponent:!0},LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:{label:"Nhìn tranh, chọn đáp án đúng",requiresContent:!0,hasDedicatedComponent:!0},LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:{label:"Nhìn tranh, điền chỗ trống, chọn đáp án",requiresContent:!0,hasDedicatedComponent:!0},LOOK_PICTURE_FILL_WORD_HINT:{label:"Nhìn tranh điền từ gợi ý",requiresContent:!0,hasDedicatedComponent:!0},LABEL_THE_PICTURE:{label:"Label the Picture",requiresContent:!0,hasDedicatedComponent:!0},READ_AND_COLOR_OBJECTS:{label:"Read and Color Objects",requiresContent:!0,hasDedicatedComponent:!0},IMAGE_OBJECT_MATCHING:{label:"Image Object Matching",requiresContent:!0,hasDedicatedComponent:!0},LISTENING_FILL_BLANK:{label:"Nghe - Điền vào chỗ trống",requiresContent:!0,hasDedicatedComponent:!1},LISTENING_COLOR:{label:"Nghe - Tô màu",requiresContent:!0,hasDedicatedComponent:!1},READING_LETTER_ARRANGE:{label:"Đọc - Sắp xếp chữ cái",requiresContent:!0,hasDedicatedComponent:!1},READING_PASSAGE_FILL:{label:"Đọc - Điền vào đoạn văn",requiresContent:!0,hasDedicatedComponent:!1},FILL_BLANK:{label:"Điền vào chỗ trống",requiresContent:!0,hasDedicatedComponent:!1},CHOOSE_THE_CORRECT_ANSWER:{label:"Choose the correct answer",requiresContent:!1,hasDedicatedComponent:!0},CHOOSE_THE_CORRECT_ANSWER_GROUP:{label:"Choose the correct answer (group)",requiresContent:!0,hasDedicatedComponent:!0},TRUE_FALSE_GROUP:{label:"Đúng/Sai (nhóm)",requiresContent:!0,hasDedicatedComponent:!0},CROSS_OUT_WORD_GROUP:{label:"Cross out word (group)",requiresContent:!0,hasDedicatedComponent:!0},WRITE_A_SHORT_LETTER:{label:"Write a Short Letter",requiresContent:!0,hasDedicatedComponent:!0},WRITE_CORRECT_VERB_FORM:{label:"Viết dạng đúng của động từ",requiresContent:!0,hasDedicatedComponent:!0},CHOOSE_CORRECT_ADJECTIVE:{label:"Chọn dạng đúng của tính từ",requiresContent:!0,hasDedicatedComponent:!0},ANSWER_THE_QUESTION:{label:"Nghe - Đọc câu hỏi và viết đáp án",requiresContent:!0,hasDedicatedComponent:!0},WORD_ORDERING:{label:"Sắp xếp từ",requiresContent:!0,hasDedicatedComponent:!0},WORD_FILL_PARAGRAPH:{label:"Điền từ vào đoạn văn",requiresContent:!0,hasDedicatedComponent:!0},MATCH_BY_WRITING_ANSWER:{label:"Ghép đáp án bằng cách viết",requiresContent:!0,hasDedicatedComponent:!0},WRITE_SENTENCES:{label:"Viết câu",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:{label:"Nghe và chọn trong nhóm đáp án",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:{label:"Nghe và chọn đối tượng trong cảnh",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:{label:"Nghe và kéo thả đối tượng vào cảnh",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_ANSWER:{label:"Nghe và trả lời (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_IMAGE_GROUP:{label:"Nghe, nhìn nhóm ảnh và trả lời (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_QUESTION_LIST:{label:"Nghe, chọn câu hỏi và trả lời (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_COMPARE_IMAGES:{label:"Nghe và So sánh điểm khác biệt (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_WITH_STORY_IMAGES:{label:"Nghe, nhìn ảnh truyện và kể lại (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_ODD_ONE_OUT:{label:"Nghe, chọn hình khác loại và giải thích (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_INFO_EXCHANGE:{label:"Trao đổi thông tin (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},READ_DISPLAYED_CONTENT:{label:"Đọc nội dung hiển thị (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},SPEAKING_DESCRIBE_IMAGE:{label:"Nhìn tranh mô tả (Speaking Part 2)",requiresContent:!0,hasDedicatedComponent:!0},SPONTANEOUS_QA:{label:"Hỏi đáp tự phát (Speaking Part 3)",requiresContent:!0,hasDedicatedComponent:!0},SPEAKING_CONVERSATION:{label:"Hội thoại Speaking (KFS Speaking)",requiresContent:!0,hasDedicatedComponent:!0},GN_SPEAKING_INTERVIEW:{label:"GN Speaking - Hỏi đáp (Virtual + Real)",requiresContent:!0,hasDedicatedComponent:!0},SPEAKING_CUE_CARD:{label:"IELTS Cue Card (Speaking Part 2)",requiresContent:!0,hasDedicatedComponent:!0},WORD_FILL_STRUCTURED_FORM:{label:"Điền từ vào biểu mẫu có cấu trúc",requiresContent:!0,hasDedicatedComponent:!0}};export const QUESTION_TYPES_WITH_DEDICATED_COMPONENTS=["FILL_IN_BLANK","ARRANGE_LETTERS_INTO_WORDS","WRITE_SHORT_PARAGRAPH","FILL_MISSING_WORDS_IN_GRID","LOOK_PICTURE_CHOOSE_CORRECT_ANSWER","LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER","LOOK_PICTURE_FILL_WORD_HINT","LABEL_THE_PICTURE","READ_AND_COLOR_OBJECTS","IMAGE_OBJECT_MATCHING","READ_PASSAGE_AND_ANSWER_QUESTIONS","CHOOSE_THE_CORRECT_ANSWER","CHOOSE_THE_CORRECT_ANSWER_GROUP","TRUE_FALSE_GROUP","CROSS_OUT_WORD_GROUP","WRITE_A_SHORT_LETTER","WRITE_CORRECT_VERB_FORM","CHOOSE_CORRECT_ADJECTIVE","ANSWER_THE_QUESTION","TRUE_FALSE","WORD_ORDERING","WORD_FILL_PARAGRAPH","MATCH_BY_WRITING_ANSWER","WRITE_SENTENCES","LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP","LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE","LISTEN_AND_DRAG_OBJECTS_INTO_SCENE","LISTEN_AND_SPEAK_ANSWER","LISTEN_AND_SPEAK_IMAGE_GROUP","LISTEN_AND_SPEAK_QUESTION_LIST","LISTEN_AND_SPEAK_COMPARE_IMAGES","LISTEN_AND_SPEAK_WITH_STORY_IMAGES","LISTEN_AND_SPEAK_ODD_ONE_OUT","LISTEN_AND_SPEAK_INFO_EXCHANGE","READ_DISPLAYED_CONTENT","SPEAKING_DESCRIBE_IMAGE","SPONTANEOUS_QA","SPEAKING_CONVERSATION","GN_SPEAKING_INTERVIEW","SPEAKING_CUE_CARD","WORD_FILL_STRUCTURED_FORM"];export function getQuestionTypeLabel(e){return QUESTION_TYPE_CONFIG[e]?.label||e}
1
+ export const QUESTION_TYPE_CONFIG={FILL_IN_BLANK:{label:"Điền vào chỗ trống",requiresContent:!0,hasDedicatedComponent:!0},TRUE_FALSE:{label:"Đúng/Sai",requiresContent:!0,hasDedicatedComponent:!0},MATCHING:{label:"Nối",requiresContent:!0,hasDedicatedComponent:!1},ORDERING:{label:"Sắp xếp",requiresContent:!0,hasDedicatedComponent:!1},LISTENING:{label:"Nghe",requiresContent:!0,hasDedicatedComponent:!1},COLORING:{label:"Tô màu",requiresContent:!0,hasDedicatedComponent:!1},ESSAY:{label:"Viết luận",requiresContent:!0,hasDedicatedComponent:!1},LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE:{label:"Nghe - Điền chỗ trống",requiresContent:!0,hasDedicatedComponent:!1},LISTEN_AND_TICK_ANSWER:{label:"Nghe - Tick đáp án",requiresContent:!0,hasDedicatedComponent:!1},LISTEN_AND_FILL_NAME_NUMBER:{label:"Nghe - Điền tên/số",requiresContent:!0,hasDedicatedComponent:!1},TICK_TRUE_OR_FALSE:{label:"Đọc - Tick/Cross",requiresContent:!0,hasDedicatedComponent:!1},TICK_YES_OR_NO:{label:"Đọc - Tick/Cross (Yes/No)",requiresContent:!0,hasDedicatedComponent:!1},READING_TICK_CROSS:{label:"Đọc - Tick/Cross (Reading)",requiresContent:!0,hasDedicatedComponent:!1},ARRANGE_LETTERS_INTO_WORDS:{label:"Đọc - Sắp xếp chữ cái",requiresContent:!0,hasDedicatedComponent:!0},READ_PASSAGE_AND_COMPLETE_STORY:{label:"Đọc - Điền vào đoạn văn",requiresContent:!0,hasDedicatedComponent:!1},READ_PASSAGE_AND_ANSWER_QUESTIONS:{label:"Đọc đoạn văn và trả lời câu hỏi",requiresContent:!0,hasDedicatedComponent:!0},CLOZE_TEST_WITH_TICK_BOX:{label:"Cloze test với tick box",requiresContent:!0,hasDedicatedComponent:!1},READ_AND_CHOOSE_APPROPRIATE_WORD:{label:"Đọc - Chọn từ phù hợp",requiresContent:!0,hasDedicatedComponent:!1},WRITE_SHORT_PARAGRAPH:{label:"Viết - Đoạn văn ngắn",requiresContent:!0,hasDedicatedComponent:!0},FILL_MISSING_WORDS_IN_GRID:{label:"Điền từ còn thiếu vào grid",requiresContent:!0,hasDedicatedComponent:!0},LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:{label:"Nhìn tranh, chọn đáp án đúng",requiresContent:!0,hasDedicatedComponent:!0},LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:{label:"Nhìn tranh, điền chỗ trống, chọn đáp án",requiresContent:!0,hasDedicatedComponent:!0},LOOK_PICTURE_FILL_WORD_HINT:{label:"Nhìn tranh điền từ gợi ý",requiresContent:!0,hasDedicatedComponent:!0},LABEL_THE_PICTURE:{label:"Label the Picture",requiresContent:!0,hasDedicatedComponent:!0},READ_AND_COLOR_OBJECTS:{label:"Read and Color Objects",requiresContent:!0,hasDedicatedComponent:!0},IMAGE_OBJECT_MATCHING:{label:"Image Object Matching",requiresContent:!0,hasDedicatedComponent:!0},LISTENING_FILL_BLANK:{label:"Nghe - Điền vào chỗ trống",requiresContent:!0,hasDedicatedComponent:!1},LISTENING_COLOR:{label:"Nghe - Tô màu",requiresContent:!0,hasDedicatedComponent:!1},READING_LETTER_ARRANGE:{label:"Đọc - Sắp xếp chữ cái",requiresContent:!0,hasDedicatedComponent:!1},READING_PASSAGE_FILL:{label:"Đọc - Điền vào đoạn văn",requiresContent:!0,hasDedicatedComponent:!1},FILL_BLANK:{label:"Điền vào chỗ trống",requiresContent:!0,hasDedicatedComponent:!1},CHOOSE_THE_CORRECT_ANSWER:{label:"Choose the correct answer",requiresContent:!1,hasDedicatedComponent:!0},CHOOSE_THE_CORRECT_ANSWER_GROUP:{label:"Choose the correct answer (group)",requiresContent:!0,hasDedicatedComponent:!0},TRUE_FALSE_GROUP:{label:"Đúng/Sai (nhóm)",requiresContent:!0,hasDedicatedComponent:!0},CROSS_OUT_WORD_GROUP:{label:"Cross out word (group)",requiresContent:!0,hasDedicatedComponent:!0},SPONTANEOUS_QA_GROUP:{label:"Hỏi đáp tự phát (nhóm)",requiresContent:!0,hasDedicatedComponent:!1},WORD_ORDER_AND_MATCH_GROUP:{label:"Sắp xếp từ và nối nhãn (nhóm)",requiresContent:!0,hasDedicatedComponent:!1},WRITE_A_SHORT_LETTER:{label:"Write a Short Letter",requiresContent:!0,hasDedicatedComponent:!0},WRITE_CORRECT_VERB_FORM:{label:"Viết dạng đúng của động từ",requiresContent:!0,hasDedicatedComponent:!0},CHOOSE_CORRECT_ADJECTIVE:{label:"Chọn dạng đúng của tính từ",requiresContent:!0,hasDedicatedComponent:!0},ANSWER_THE_QUESTION:{label:"Nghe - Đọc câu hỏi và viết đáp án",requiresContent:!0,hasDedicatedComponent:!0},WORD_ORDERING:{label:"Sắp xếp từ",requiresContent:!0,hasDedicatedComponent:!0},WORD_FILL_PARAGRAPH:{label:"Điền từ vào đoạn văn",requiresContent:!0,hasDedicatedComponent:!0},MATCH_BY_WRITING_ANSWER:{label:"Ghép đáp án bằng cách viết",requiresContent:!0,hasDedicatedComponent:!0},WRITE_SENTENCES:{label:"Viết câu",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:{label:"Nghe và chọn trong nhóm đáp án",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:{label:"Nghe và chọn đối tượng trong cảnh",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:{label:"Nghe và kéo thả đối tượng vào cảnh",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_ANSWER:{label:"Nghe và trả lời (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_IMAGE_GROUP:{label:"Nghe, nhìn nhóm ảnh và trả lời (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_QUESTION_LIST:{label:"Nghe, chọn câu hỏi và trả lời (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_COMPARE_IMAGES:{label:"Nghe và So sánh điểm khác biệt (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_WITH_STORY_IMAGES:{label:"Nghe, nhìn ảnh truyện và kể lại (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_ODD_ONE_OUT:{label:"Nghe, chọn hình khác loại và giải thích (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},LISTEN_AND_SPEAK_INFO_EXCHANGE:{label:"Trao đổi thông tin (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},READ_DISPLAYED_CONTENT:{label:"Đọc nội dung hiển thị (Speaking)",requiresContent:!0,hasDedicatedComponent:!0},SPEAKING_DESCRIBE_IMAGE:{label:"Nhìn tranh mô tả (Speaking Part 2)",requiresContent:!0,hasDedicatedComponent:!0},SPONTANEOUS_QA:{label:"Hỏi đáp tự phát (Speaking Part 3)",requiresContent:!0,hasDedicatedComponent:!0},SPEAKING_CONVERSATION:{label:"Hội thoại Speaking (KFS Speaking)",requiresContent:!0,hasDedicatedComponent:!0},GN_SPEAKING_INTERVIEW:{label:"GN Speaking - Hỏi đáp (Virtual + Real)",requiresContent:!0,hasDedicatedComponent:!0},SPEAKING_CUE_CARD:{label:"IELTS Cue Card (Speaking Part 2)",requiresContent:!0,hasDedicatedComponent:!0},WORD_FILL_STRUCTURED_FORM:{label:"Điền từ vào biểu mẫu có cấu trúc",requiresContent:!0,hasDedicatedComponent:!0}};export const QUESTION_TYPES_WITH_DEDICATED_COMPONENTS=["FILL_IN_BLANK","ARRANGE_LETTERS_INTO_WORDS","WRITE_SHORT_PARAGRAPH","FILL_MISSING_WORDS_IN_GRID","LOOK_PICTURE_CHOOSE_CORRECT_ANSWER","LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER","LOOK_PICTURE_FILL_WORD_HINT","LABEL_THE_PICTURE","READ_AND_COLOR_OBJECTS","IMAGE_OBJECT_MATCHING","READ_PASSAGE_AND_ANSWER_QUESTIONS","CHOOSE_THE_CORRECT_ANSWER","CHOOSE_THE_CORRECT_ANSWER_GROUP","TRUE_FALSE_GROUP","CROSS_OUT_WORD_GROUP","WRITE_A_SHORT_LETTER","WRITE_CORRECT_VERB_FORM","CHOOSE_CORRECT_ADJECTIVE","ANSWER_THE_QUESTION","TRUE_FALSE","WORD_ORDERING","WORD_FILL_PARAGRAPH","MATCH_BY_WRITING_ANSWER","WRITE_SENTENCES","LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP","LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE","LISTEN_AND_DRAG_OBJECTS_INTO_SCENE","LISTEN_AND_SPEAK_ANSWER","LISTEN_AND_SPEAK_IMAGE_GROUP","LISTEN_AND_SPEAK_QUESTION_LIST","LISTEN_AND_SPEAK_COMPARE_IMAGES","LISTEN_AND_SPEAK_WITH_STORY_IMAGES","LISTEN_AND_SPEAK_ODD_ONE_OUT","LISTEN_AND_SPEAK_INFO_EXCHANGE","READ_DISPLAYED_CONTENT","SPEAKING_DESCRIBE_IMAGE","SPONTANEOUS_QA","SPEAKING_CONVERSATION","GN_SPEAKING_INTERVIEW","SPEAKING_CUE_CARD","WORD_FILL_STRUCTURED_FORM"];export function getQuestionTypeLabel(e){return QUESTION_TYPE_CONFIG[e]?.label||e}
@@ -24,7 +24,7 @@ export interface WordFillStructuredFormData {
24
24
  explanation?: string;
25
25
  caseSensitive?: boolean;
26
26
  /** Free-type width hint when not WITH_OPTIONS. */
27
- blankStyle?: 'letter' | 'word' | 'phrase';
27
+ blankStyle?: 'letter' | 'word' | 'phrase' | 'translate';
28
28
  /** Discriminates wordBank shape (0024 / 0025). */
29
29
  viewMode?: WfsfViewMode | null;
30
30
  /**
@@ -1,2 +1,3 @@
1
1
  export * from './ChooseTheCorrectAnswerGroupCreator';
2
2
  export * from './ChooseTheCorrectAnswerGroupClient';
3
+ export * from './map-choose-the-correct-answer-group-data';
@@ -1 +1 @@
1
- export*from"./ChooseTheCorrectAnswerGroupCreator";export*from"./ChooseTheCorrectAnswerGroupClient";
1
+ export*from"./ChooseTheCorrectAnswerGroupCreator";export*from"./ChooseTheCorrectAnswerGroupClient";export*from"./map-choose-the-correct-answer-group-data";
@@ -0,0 +1,4 @@
1
+ import type { ChooseTheCorrectAnswerGroupData, ChooseTheCorrectAnswerGroupItemData } from '../../_shared/types/choose-the-correct-answer-group.type';
2
+ export declare function createEmptyChooseAnswerGroupItem(questionNumber: number): ChooseTheCorrectAnswerGroupItemData;
3
+ export declare function mapChooseAnswerGroupItem(item: unknown, index: number, fallbackAnswer?: string): ChooseTheCorrectAnswerGroupItemData;
4
+ export declare function mapQuestionToChooseAnswerGroupData(question: Record<string, unknown> | null | undefined): ChooseTheCorrectAnswerGroupData;
@@ -0,0 +1 @@
1
+ function t(t){return function(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}(t)?t:{}}function e(t,e=""){return"string"==typeof t?t:e}export function createEmptyChooseAnswerGroupItem(t){return{question:"",optionType:"text",options:[{id:"opt-1",text:""},{id:"opt-2",text:""},{id:"opt-3",text:""}],correctAnswer:"opt-1",isExample:!1,points:1,questionNumber:t}}export function mapChooseAnswerGroupItem(o,n,r){const i=t(o),s=t(i.content),p=t(i.correctAnswer),a=Array.isArray(s.options)?s.options:[],u=a.length>=2?a.map((o,n)=>{const r=t(o);return{id:e(r.id,`opt-${n+1}`),text:e(r.text)}}):[{id:"opt-1",text:""},{id:"opt-2",text:""},{id:"opt-3",text:""}],c=!0===i.isExample;return{question:e(s.question)||e(i.question),optionType:"image"===e(s.optionType)?"image":"text",options:u,correctAnswer:e(p.answer)||r||u[0]?.id||"opt-1",isExample:c,points:c?0:"number"==typeof i.points?i.points:1,questionNumber:"number"==typeof i.questionNumber?i.questionNumber:n+1}}export function mapQuestionToChooseAnswerGroupData(o){const n=t(o),r=t(n.content),i=t(n.answer),s=t(n.correctAnswer??n.correct_answer??i.correctAnswer),p=String(n.explanation||i.explanation||""),a=Array.isArray(s.answer)?s.answer.map(t=>"string"==typeof t?t:void 0):[];if(Array.isArray(r.items)){const o=r.items.map((t,e)=>mapChooseAnswerGroupItem(t,e,a[e]));return{instruction:e(t(r.meta).instruction)||"Choose the correct answer.",items:o,explanation:p,points:o.reduce((t,e)=>e.isExample?t:t+(e.points||0),0)}}return Array.isArray(i.items)?{instruction:e(i.instruction,"Choose the correct answer."),items:i.items.map((t,e)=>mapChooseAnswerGroupItem(t,e,a[e])),explanation:p,points:"number"==typeof i.points?i.points:void 0}:{instruction:"Choose the correct answer.",items:[createEmptyChooseAnswerGroupItem(1)],explanation:p,points:1}}
@@ -1 +1 @@
1
- import{ChooseTheCorrectAnswerGroupCreator as t}from"./ChooseTheCorrectAnswerGroupCreator";function e(t){return function(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}(t)?t:{}}function n(t,e=""){return"string"==typeof t?t:e}export const chooseTheCorrectAnswerGroupRegistration={component:t,transformInitialData:t=>{const o=t?.content,i=t?.answer,r=t?.correctAnswer||i?.correctAnswer||{},s=t?.explanation||i?.explanation||"",a=Array.isArray(r?.answer)?r.answer:[];if(Array.isArray(o?.items)){const t=o.items.map((t,o)=>function(t,o,i){const r=e(t),s=e(r.content),a=e(r.correctAnswer),p=Array.isArray(s.options)?s.options:[],c=p.length>=2?p.map((t,o)=>{const i=e(t);return{id:n(i.id,`opt-${o+1}`),text:n(i.text)}}):[{id:"opt-1",text:""},{id:"opt-2",text:""},{id:"opt-3",text:""}],u=!0===r.isExample;return{question:n(s.question)||n(r.question),optionType:"image"===n(s.optionType)?"image":"text",options:c,correctAnswer:n(a.answer)||i||c[0]?.id||"opt-1",isExample:u,points:u?0:"number"==typeof r.points?r.points:1,questionNumber:"number"==typeof r.questionNumber?r.questionNumber:o+1}}(t,o,a[o]));return{instruction:o?.meta?.instruction||"Choose the correct answer.",items:t,explanation:s,points:t.reduce((t,e)=>e.isExample?t:t+(e.points||0),0)}}return Array.isArray(i?.items)?{instruction:i.instruction||"Choose the correct answer.",items:i.items,explanation:s,points:i.points}:{instruction:"Choose the correct answer.",items:[(p=1,{question:"",optionType:"text",options:[{id:"opt-1",text:""},{id:"opt-2",text:""},{id:"opt-3",text:""}],correctAnswer:"opt-1",isExample:!1,points:1,questionNumber:p})],explanation:s,points:1};var p},getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef}),wrapOnSave:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}},wrapOnChange:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}}};
1
+ import{ChooseTheCorrectAnswerGroupCreator as t}from"./ChooseTheCorrectAnswerGroupCreator";import{mapQuestionToChooseAnswerGroupData as e}from"./map-choose-the-correct-answer-group-data";export const chooseTheCorrectAnswerGroupRegistration={component:t,transformInitialData:t=>e(t),getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef}),wrapOnSave:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}},wrapOnChange:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}}};
@@ -2,3 +2,4 @@ export * from './CrossOutWordGroupCreator';
2
2
  export * from './CrossOutWordGroupClient';
3
3
  export * from './transform';
4
4
  export * from './register';
5
+ export * from './map-cross-out-word-group-data';
@@ -1 +1 @@
1
- export*from"./CrossOutWordGroupCreator";export*from"./CrossOutWordGroupClient";export*from"./transform";export*from"./register";
1
+ export*from"./CrossOutWordGroupCreator";export*from"./CrossOutWordGroupClient";export*from"./transform";export*from"./register";export*from"./map-cross-out-word-group-data";
@@ -0,0 +1,6 @@
1
+ import type { CrossOutWordGroupData, CrossOutWordGroupItemAnswer, CrossOutWordGroupItemData, CrossOutWordItemMode } from '../../_shared/types/cross-out-word-group.type';
2
+ export declare const DEFAULT_CROSS_OUT_INSTRUCTION = "Cross out the extra or incorrect word. If the word is incorrect, write the correct word. If the line is correct, leave it blank.";
3
+ export declare function createCrossOutAnswer(type: CrossOutWordItemMode, crossOut?: string, replaceWith?: string): CrossOutWordGroupItemAnswer;
4
+ export declare function createEmptyCrossOutItem(questionNumber: number): CrossOutWordGroupItemData;
5
+ export declare function mapCrossOutWordGroupItem(item: unknown, index: number, fallbackAnswer?: CrossOutWordGroupItemAnswer): CrossOutWordGroupItemData;
6
+ export declare function mapQuestionToCrossOutWordGroupData(question: Record<string, unknown> | null | undefined): CrossOutWordGroupData;
@@ -0,0 +1 @@
1
+ export const DEFAULT_CROSS_OUT_INSTRUCTION="Cross out the extra or incorrect word. If the word is incorrect, write the correct word. If the line is correct, leave it blank.";function t(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function r(r){return t(r)?r:{}}function e(t,r=""){return"string"==typeof t?t:r}function n(t){return"CROSS_OUT_AND_REPLACE"===t?"CROSS_OUT_AND_REPLACE":"CROSS_OUT"}export function createCrossOutAnswer(t,r="",e=""){return"CROSS_OUT_AND_REPLACE"===t?{type:t,crossOut:r,replaceWith:e}:{type:"CROSS_OUT",crossOut:r}}export function createEmptyCrossOutItem(t){return{type:"CROSS_OUT",question:"",correctAnswer:createCrossOutAnswer("CROSS_OUT"),isExample:!1,points:1,questionNumber:t}}export function mapCrossOutWordGroupItem(o,s,i){const u=r(o),c=r(u.content),p=r(u.correctAnswer),a=t(p.answer)?p.answer:t(i)?i:{},O=n(c.type||a.type),m=!0===u.isExample;return{type:O,question:e(c.question)||e(u.question),correctAnswer:createCrossOutAnswer(O,e(a.crossOut),e(a.replaceWith)),isExample:m,points:m?0:"number"==typeof u.points?u.points:1,questionNumber:"number"==typeof u.questionNumber?u.questionNumber:s+1}}export function mapQuestionToCrossOutWordGroupData(o){const s=r(o),i=r(s.content),u=r(s.answer),c=r(s.correctAnswer??s.correct_answer??u.correctAnswer),p=String(s.explanation||u.explanation||""),a=Array.isArray(c.answer)?c.answer.map(r=>{if(t(r))return createCrossOutAnswer(n(r.type),e(r.crossOut),e(r.replaceWith))}):[];if(Array.isArray(i.items)){const t=i.items.map((t,r)=>mapCrossOutWordGroupItem(t,r,a[r]));return{instruction:e(r(i.meta).instruction)||DEFAULT_CROSS_OUT_INSTRUCTION,items:t,explanation:p,points:t.reduce((t,r)=>r.isExample?t:t+(r.points||0),0)}}return Array.isArray(u.items)?{instruction:e(u.instruction,DEFAULT_CROSS_OUT_INSTRUCTION),items:u.items.map((t,r)=>mapCrossOutWordGroupItem(t,r,a[r])),explanation:p,points:"number"==typeof u.points?u.points:void 0}:{instruction:DEFAULT_CROSS_OUT_INSTRUCTION,items:[createEmptyCrossOutItem(1)],explanation:p,points:1}}
@@ -1 +1 @@
1
- import{CrossOutWordGroupCreator as t}from"./CrossOutWordGroupCreator";const e="Cross out the extra or incorrect word. If the word is incorrect, write the correct word. If the line is correct, leave it blank.";function n(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}function r(t){return n(t)?t:{}}function o(t,e=""){return"string"==typeof t?t:e}function s(t,e="",n=""){return"CROSS_OUT_AND_REPLACE"===t?{type:t,crossOut:e,replaceWith:n}:{type:"CROSS_OUT",crossOut:e}}export const crossOutWordGroupRegistration={component:t,transformInitialData:t=>{const i=t?.content,a=t?.answer,c=t?.correctAnswer||a?.correctAnswer||{},p=t?.explanation||a?.explanation||"",u=Array.isArray(c?.answer)?c.answer:[];if(Array.isArray(i?.items)){const t=i.items.map((t,e)=>function(t,e,i){const a=r(t),c=r(a.content),p=r(a.correctAnswer),u=n(p.answer)?p.answer:n(i)?i:{},l=function(t){return"CROSS_OUT_AND_REPLACE"===t?"CROSS_OUT_AND_REPLACE":"CROSS_OUT"}(c.type||u.type),y=!0===a.isExample;return{type:l,question:o(c.question)||o(a.question),correctAnswer:s(l,o(u.crossOut),o(u.replaceWith)),isExample:y,points:y?0:"number"==typeof a.points?a.points:1,questionNumber:"number"==typeof a.questionNumber?a.questionNumber:e+1}}(t,e,u[e]));return{instruction:i?.meta?.instruction||e,items:t,explanation:p,points:t.reduce((t,e)=>e.isExample?t:t+(e.points||0),0)}}return Array.isArray(a?.items)?{instruction:a.instruction||e,items:a.items,explanation:p,points:a.points}:{instruction:e,items:[(l=1,{type:"CROSS_OUT",question:"",correctAnswer:s("CROSS_OUT"),isExample:!1,points:1,questionNumber:l})],explanation:p,points:1};var l},getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef}),wrapOnSave:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}},wrapOnChange:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}}};
1
+ import{CrossOutWordGroupCreator as t}from"./CrossOutWordGroupCreator";import{mapQuestionToCrossOutWordGroupData as e}from"./map-cross-out-word-group-data";export const crossOutWordGroupRegistration={component:t,transformInitialData:t=>e(t),getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef}),wrapOnSave:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}},wrapOnChange:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}}};
@@ -2,3 +2,4 @@ export * from './TrueFalseGroupCreator';
2
2
  export * from './TrueFalseGroupClient';
3
3
  export * from './transform';
4
4
  export * from './register';
5
+ export * from './map-true-false-group-data';
@@ -1 +1 @@
1
- export*from"./TrueFalseGroupCreator";export*from"./TrueFalseGroupClient";export*from"./transform";export*from"./register";
1
+ export*from"./TrueFalseGroupCreator";export*from"./TrueFalseGroupClient";export*from"./transform";export*from"./register";export*from"./map-true-false-group-data";
@@ -0,0 +1,4 @@
1
+ import type { TrueFalseGroupData, TrueFalseGroupItemData } from '../../_shared/types/true-false-group.type';
2
+ export declare function createEmptyTrueFalseGroupItem(questionNumber: number): TrueFalseGroupItemData;
3
+ export declare function mapTrueFalseGroupItem(item: unknown, index: number, fallbackAnswer?: boolean): TrueFalseGroupItemData;
4
+ export declare function mapQuestionToTrueFalseGroupData(question: Record<string, unknown> | null | undefined): TrueFalseGroupData;
@@ -0,0 +1 @@
1
+ function e(e){return function(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e)?e:{}}function t(e,t=""){return"string"==typeof e?e:t}export function createEmptyTrueFalseGroupItem(e){return{question:"",correctAnswer:!0,isExample:!1,points:1,questionNumber:e}}export function mapTrueFalseGroupItem(r,n,o){const i=e(r),s=e(i.content),a=e(i.correctAnswer),u=!0===i.isExample,p="boolean"==typeof a.answer?a.answer:"boolean"!=typeof o||o;return{question:t(s.question)||t(i.question),correctAnswer:p,isExample:u,points:u?0:"number"==typeof i.points?i.points:1,questionNumber:"number"==typeof i.questionNumber?i.questionNumber:n+1}}export function mapQuestionToTrueFalseGroupData(r){const n=e(r),o=e(n.content),i=e(n.answer),s=e(n.correctAnswer??n.correct_answer??i.correctAnswer),a=String(n.explanation||i.explanation||""),u=Array.isArray(s.answer)?s.answer.map(e=>"boolean"==typeof e?e:void 0):[];if(Array.isArray(o.items)){const r=o.items.map((e,t)=>mapTrueFalseGroupItem(e,t,u[t]));return{instruction:t(e(o.meta).instruction)||"Tick True or False.",items:r,explanation:a,points:r.reduce((e,t)=>t.isExample?e:e+(t.points||0),0)}}return Array.isArray(i.items)?{instruction:t(i.instruction,"Tick True or False."),items:i.items.map((e,t)=>mapTrueFalseGroupItem(e,t,u[t])),explanation:a,points:"number"==typeof i.points?i.points:void 0}:{instruction:"Tick True or False.",items:[createEmptyTrueFalseGroupItem(1)],explanation:a,points:1}}
@@ -1 +1 @@
1
- import{TrueFalseGroupCreator as t}from"./TrueFalseGroupCreator";function e(t){return function(t){return"object"==typeof t&&null!==t&&!Array.isArray(t)}(t)?t:{}}function n(t,e=""){return"string"==typeof t?t:e}export const trueFalseGroupRegistration={component:t,transformInitialData:t=>{const r=t?.content,s=t?.answer,i=t?.correctAnswer||s?.correctAnswer||{},o=t?.explanation||s?.explanation||"",a=Array.isArray(i?.answer)?i.answer:[];if(Array.isArray(r?.items)){const t=r.items.map((t,r)=>function(t,r,s){const i=e(t),o=e(i.content),a=e(i.correctAnswer),p=!0===i.isExample,u="boolean"==typeof a.answer?a.answer:"boolean"!=typeof s||s;return{question:n(o.question)||n(i.question),correctAnswer:u,isExample:p,points:p?0:"number"==typeof i.points?i.points:1,questionNumber:"number"==typeof i.questionNumber?i.questionNumber:r+1}}(t,r,a[r]));return{instruction:r?.meta?.instruction||"Tick True or False.",items:t,explanation:o,points:t.reduce((t,e)=>e.isExample?t:t+(e.points||0),0)}}return Array.isArray(s?.items)?{instruction:s.instruction||"Tick True or False.",items:s.items,explanation:o,points:s.points}:{instruction:"Tick True or False.",items:[(p=1,{question:"",correctAnswer:!0,isExample:!1,points:1,questionNumber:p})],explanation:o,points:1};var p},getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef}),wrapOnSave:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}},wrapOnChange:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}}};
1
+ import{TrueFalseGroupCreator as t}from"./TrueFalseGroupCreator";import{mapQuestionToTrueFalseGroupData as e}from"./map-true-false-group-data";export const trueFalseGroupRegistration={component:t,transformInitialData:t=>e(t),getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef}),wrapOnSave:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}},wrapOnChange:(t,e)=>{const n=t;return{type:e.questionType,content:e.state.content,points:n.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:n,explanation:n.explanation||""}}};
@@ -1,2 +1,3 @@
1
1
  export { WordFillStructuredFormCreator } from './WordFillStructuredFormCreator';
2
2
  export { WordFillStructuredFormClient } from './WordFillStructuredFormClient';
3
+ export { mapQuestionToWfsfData } from './map-wfsf-question-data';
@@ -1 +1 @@
1
- export{WordFillStructuredFormCreator}from"./WordFillStructuredFormCreator";export{WordFillStructuredFormClient}from"./WordFillStructuredFormClient";
1
+ export{WordFillStructuredFormCreator}from"./WordFillStructuredFormCreator";export{WordFillStructuredFormClient}from"./WordFillStructuredFormClient";export{mapQuestionToWfsfData}from"./map-wfsf-question-data";
@@ -0,0 +1,7 @@
1
+ import type { WordFillStructuredFormData } from '../../_shared/types/word-fill-structured-form.type';
2
+ export declare function extractWfsfBlankKeys(html: string): string[];
3
+ /**
4
+ * Map preview / exam / creator payloads onto WordFillStructuredFormClient data.
5
+ * School preview puts answers on `answer` + `correctAnswer`; creator uses `answer`.
6
+ */
7
+ export declare function mapQuestionToWfsfData(question: Record<string, unknown> | null | undefined): WordFillStructuredFormData;
@@ -0,0 +1 @@
1
+ import{detectWfsfBankMode as t,normalizeOptionsWordBank as n,normalizeSharedWordBank as e}from"./wfsf-word-bank.utils";function r(t){return t&&"object"==typeof t&&!Array.isArray(t)?t:{}}function o(t){const n=r(t),e={};for(const[t,r]of Object.entries(n))"string"==typeof r&&(e[t]=r);return e}function i(t){if("letter"===t||"word"===t||"phrase"===t||"translate"===t)return t}export function extractWfsfBlankKeys(t){const n=new Set,e=/\{([\w\d]+)\}/g;let r;for(;null!==(r=e.exec(t));)n.add(r[1]);return Array.from(n).sort((t,n)=>{const e=parseInt(t,10),r=parseInt(n,10);return Number.isNaN(e)||Number.isNaN(r)?t.localeCompare(n):e-r})}export function mapQuestionToWfsfData(a){const s=r(a),l=r(s.content),c=r(s.answer),u=r(s.correctAnswer??s.correct_answer??c.correctAnswer??c),p=s.total_points?parseFloat(String(s.total_points)):c.points??s.points??1,g=Number.isFinite(Number(p))?Number(p):1,f=String(c.instruction||s.instruction||s.explanation||""),S=String(c.explanation||""),w=function(t,n){const e=t.content||n.content||"";if("string"==typeof e&&e)return e;const r=t.blocks||n.blocks;return r?.length?r.map(t=>(t.heading?`<h3>${t.heading}</h3>`:"")+(t.content||"")).join(""):"string"==typeof e?e:""}(l,c),m=l.wordBank??c.wordBank,b=l.viewMode??c.viewMode,d=t(b,m),y=extractWfsfBlankKeys(String(w||"")),k="FREE_TYPE"===d?void 0:d,N="WITH_WORD_BANK"===d?e(m):"WITH_OPTIONS"===d?n(m,y):void 0,v=r(r(s.questionGroup).payload);return{title:String(l.title||c.title||""),imageUrl:String(l.imageUrl||c.imageUrl||""),content:w,answers:o(u.answers||c.answers),caseSensitive:Boolean(u.caseSensitive??c.caseSensitive??!1),blankStyle:i(l.blankStyle??c.blankStyle),viewMode:k,wordBank:N,instruction:f,explanation:S,points:g,groupTitle:String(v.title||c.groupTitle||""),groupSubtitle:String(v.subtitle||c.groupSubtitle||""),groupContent:String(v.content||c.groupContent||""),groupImageUrl:String(v.imageUrl||c.groupImageUrl||"")}}
@@ -1 +1 @@
1
- import{WordFillStructuredFormCreator as t}from"./WordFillStructuredFormCreator";import{detectWfsfBankMode as e,normalizeOptionsWordBank as n,normalizeSharedWordBank as o}from"./wfsf-word-bank.utils";export const wordFillStructuredFormRegistration={component:t,transformInitialData:t=>{const i=t?.content||{},a=t?.answer||{},r=t?.correctAnswer||a?.correctAnswer||{},s=t?.total_points?parseFloat(t.total_points):a?.points||t?.points||1,l=String(a?.instruction||t?.instruction||t?.explanation||""),p=String(a?.explanation||"");let u=i?.content||a?.content||"";const c=i?.blocks||a?.blocks;!u&&c?.length&&(u=c.map(t=>(t.heading?`<h3>${t.heading}</h3>`:"")+(t.content||"")).join(""));const g=i?.wordBank??a?.wordBank,d=e(i?.viewMode??a?.viewMode,g),w=function(t){const e=new Set,n=/\{([\w\d]+)\}/g;let o;for(;null!==(o=n.exec(t));)e.add(o[1]);return Array.from(e).sort((t,e)=>{const n=parseInt(t,10),o=parseInt(e,10);return Number.isNaN(n)||Number.isNaN(o)?t.localeCompare(e):n-o})}(String(u||""));let f=g,m="FREE_TYPE"===d?void 0:d;"WITH_WORD_BANK"===d?(f=o(g),m="WITH_WORD_BANK"):"WITH_OPTIONS"===d?(f=n(g,w),m="WITH_OPTIONS"):f=void 0;const v=t?.questionGroup?.payload;return{title:i?.title||a?.title||"",imageUrl:i?.imageUrl||a?.imageUrl||"",content:u,answers:r?.answers||a?.answers||{},caseSensitive:r?.caseSensitive??a?.caseSensitive??!1,blankStyle:i?.blankStyle??a?.blankStyle,viewMode:m,wordBank:f,instruction:l,explanation:p,points:s,groupTitle:v?.title||a?.groupTitle||"",groupSubtitle:v?.subtitle||a?.groupSubtitle||"",groupContent:v?.content||a?.groupContent||"",groupImageUrl:v?.imageUrl||a?.groupImageUrl||""}},getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef,groupConfig:t.groupConfig,groupType:t.groupType,groups:t.groups,questionIndexInPart:t.questionIndexInPart,partId:t.partId,viewMode:t.viewMode}),wrapOnSave:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""}),wrapOnChange:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""})};
1
+ import{WordFillStructuredFormCreator as t}from"./WordFillStructuredFormCreator";import{mapQuestionToWfsfData as e}from"./map-wfsf-question-data";export const wordFillStructuredFormRegistration={component:t,transformInitialData:t=>e(t),getExtraProps:t=>({onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef,groupConfig:t.groupConfig,groupType:t.groupType,groups:t.groups,questionIndexInPart:t.questionIndexInPart,partId:t.partId,viewMode:t.viewMode}),wrapOnSave:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""}),wrapOnChange:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""})};
@@ -1 +1 @@
1
- export const transformWordFillStructuredForm=e=>{const t=e.answer;if(!t?.content||!t?.answers)return{apiContent:{},apiCorrectAnswer:{}};const r=!!(t.groupTitle||t.groupSubtitle||t.groupContent||t.groupImageUrl),n={title:t.title||"",imageUrl:t.imageUrl||"",content:t.content};"letter"!==t.blankStyle&&"word"!==t.blankStyle&&"phrase"!==t.blankStyle||(n.blankStyle=t.blankStyle),"WITH_OPTIONS"!==t.viewMode&&"WITH_WORD_BANK"!==t.viewMode||(n.viewMode=t.viewMode),Array.isArray(t.wordBank)&&(n.wordBank=t.wordBank);return{apiContent:n,apiCorrectAnswer:{answers:t.answers,caseSensitive:t.caseSensitive??!1},basicGroupData:r?{title:t.groupTitle||"",subtitle:t.groupSubtitle||"",content:t.groupContent||"",imageUrl:t.groupImageUrl||""}:void 0,groupNumber:t.groupNumber||1}};
1
+ export const transformWordFillStructuredForm=e=>{const t=e.answer;if(!t?.content||!t?.answers)return{apiContent:{},apiCorrectAnswer:{}};const r=!!(t.groupTitle||t.groupSubtitle||t.groupContent||t.groupImageUrl),n={title:t.title||"",imageUrl:t.imageUrl||"",content:t.content};"letter"!==t.blankStyle&&"word"!==t.blankStyle&&"phrase"!==t.blankStyle&&"translate"!==t.blankStyle||(n.blankStyle=t.blankStyle),"WITH_OPTIONS"!==t.viewMode&&"WITH_WORD_BANK"!==t.viewMode||(n.viewMode=t.viewMode),Array.isArray(t.wordBank)&&(n.wordBank=t.wordBank);return{apiContent:n,apiCorrectAnswer:{answers:t.answers,caseSensitive:t.caseSensitive??!1},basicGroupData:r?{title:t.groupTitle||"",subtitle:t.groupSubtitle||"",content:t.groupContent||"",imageUrl:t.groupImageUrl||""}:void 0,groupNumber:t.groupNumber||1}};
@@ -0,0 +1,8 @@
1
+ import type { Question } from '../../../shared/types/entities/question.types';
2
+ type ChooseTheCorrectAnswerGroupViewerProps = {
3
+ question: Question;
4
+ isReviewMode?: boolean;
5
+ userAnswer?: string[];
6
+ };
7
+ export declare function ChooseTheCorrectAnswerGroupViewer({ question, isReviewMode, userAnswer, }: ChooseTheCorrectAnswerGroupViewerProps): import("react").JSX.Element;
8
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e}from"react/jsx-runtime";import{ChooseTheCorrectAnswerGroupClient as r}from"../types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient";import{mapQuestionToChooseAnswerGroupData as o}from"../types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data";export function ChooseTheCorrectAnswerGroupViewer({question:s,isReviewMode:t=!1,userAnswer:c}){const n=o(s),i=t&&!c?n.items.map(e=>e.correctAnswer):c;return e(r,{questionData:n,isReviewMode:t,userAnswers:i})}
@@ -0,0 +1,7 @@
1
+ import type { Question } from '../../../shared/types/entities/question.types';
2
+ type CrossOutWordGroupViewerProps = {
3
+ question: Question;
4
+ isReviewMode?: boolean;
5
+ };
6
+ export declare function CrossOutWordGroupViewer({ question, isReviewMode, }: CrossOutWordGroupViewerProps): import("react").JSX.Element;
7
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as o}from"react/jsx-runtime";import{CrossOutWordGroupClient as r}from"../types/cross-out-word-group/CrossOutWordGroupClient";import{mapQuestionToCrossOutWordGroupData as s}from"../types/cross-out-word-group/map-cross-out-word-group-data";export function CrossOutWordGroupViewer({question:e,isReviewMode:t=!1}){const u=s(e);return o(r,{questionData:u,isReviewMode:t,userAnswers:t?u.items.map(o=>o.correctAnswer):[]})}
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Button as s}from"../../../components/ui/button";import{Card as t,CardContent as n,CardHeader as o,CardTitle as a}from"../../../components/ui/card";import{Input as i}from"../../../components/ui/input";import{Check as c,Volume2 as l,X as d}from"lucide-react";import{useRef as u,useState as m}from"react";import{toast as p}from"sonner";import{t as g}from"../../../shared/lib/i18n";import{ReadAndColorObjectsClient as b}from"../types/read-and-color-objects/ReadAndColorObjectsClient";import{ArrangeLettersIntoWordsClient as h}from"../types/arrange-letters-into-words/ArrangeLettersIntoWordsClient";import{WriteShortParagraphClient as w}from"../types/write-short-paragraph/WriteShortParagraphClient";import{LabelThePictureClient as N}from"../types/label-the-picture/LabelThePictureClient";import{LookPictureFillWordHintClient as x}from"../types/look-picture-fill-word-hint/LookPictureFillWordHintClient";import{ReadPassageAndAnswerQuestionsClient as f}from"../types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient";import{ChooseTheCorrectAnswerClient as C}from"../types/choose-the-correct-answer/ChooseTheCorrectAnswerClient";import{WriteAShortLetterClient as A}from"../types/write-a-short-letter/WriteAShortLetterClient";import{LookPictureChooseCorrectAnswerClient as _}from"../types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient";import{LookPictureFillBlankChooseAnswerClient as y}from"../types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient";import{FillMissingWordsInGridClient as v}from"../types/fill-missing-words-in-grid/FillMissingWordsInGridClient";export function QuestionViewer({question:R,onSubmitAnswer:S,isReviewMode:E=!1,userAnswer:L}){const[T,O]=m(L||null),[I,W]=m(0),k=u(null),[q,D]=m(L||{}),P=R.answer,[U,Q]=m(L||Array(P?.blanks?.length||0).fill("")),[G,M]=m(L||{}),[H,$]=m(L||{}),[j,F]=m(L||{}),[B,K]=m(L||{}),Y=()=>{const e=R.answer.maxPlayCount||3;I>=e?p.warning(g("common.audio.maxPlayReached",{maxPlay:String(e)})):(k.current?.play(),W(I+1))},V=()=>{const t=R.answer,{blanks:n,caseSensitive:o=!1}=t;return r("div",{className:"space-y-4",children:[n.map((s,t)=>{const a=U[t]||"",l=E&&((e,r)=>{const s=n[e];return o?r===s:r.toLowerCase()===s.toLowerCase()})(t,a);return r("div",{className:"flex items-center space-x-2",children:[r("span",{className:"text-sm font-medium",children:["Chỗ trống ",t+1,":"]}),e(i,{value:a,onChange:e=>((e,r)=>{const s=[...U];s[e]=r,Q(s)})(t,e.target.value),placeholder:"Nhập đáp án...",className:"flex-1 "+(E?l?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""),disabled:E}),E&&l&&e(c,{className:"h-4 w-4 text-green-600"}),E&&!l&&a&&e(d,{className:"h-4 w-4 text-red-600"})]},t)}),!E&&e(s,{onClick:()=>{S&&S(U)},disabled:U.some(e=>""===e.trim()),children:"Nộp đáp án"})]})};return r(t,{children:[e(o,{children:r(a,{className:"flex items-center justify-between",children:[e("span",{children:"string"==typeof R.content?R.content:R.content?.question||"Câu hỏi"}),r("span",{className:"text-sm font-normal text-gray-500",children:[R.points," điểm"]})]})}),r(n,{className:"space-y-4",children:[(()=>{switch(R.type){case"CHOOSE_THE_CORRECT_ANSWER":return(()=>{const r=R.answer,{isGroup:s,questionGroup:t}=R;let n={};return n=R.content&&"object"==typeof R.content?{...R.content,correctAnswer:R.correctAnswer?.answer??-1,id:R.id,isGroup:s,groupTitle:t?.payload?.title,groupContent:t?.payload?.content,groupImageUrl:t?.payload?.imageUrl,groupNumber:t?.groupNumber}:{...r,question:R.content||r?.question,id:R.id,isGroup:s,groupTitle:t?.payload?.title,groupContent:t?.payload?.content,groupImageUrl:t?.payload?.imageUrl,groupNumber:t?.groupNumber},e(C,{questionData:n,questionConfig:R.questionConfig,onSubmit:e=>{O(e),S?.(e)},isReviewMode:E,userAnswer:L})})();case"CHOOSE_THE_CORRECT_ANSWER_GROUP":return(()=>{const s=R.content,t=R.correctAnswer||R.correct_answer||R.answer,n=s?.items||[],o=Array.isArray(t?.answer)?t.answer:[];return e("div",{className:"space-y-5",children:n.map((s,t)=>{const n=o[t],a=B[t],i=s?.content?.options||[];return r("div",{className:"space-y-2",children:[e("p",{className:"text-sm font-medium leading-relaxed",children:s?.content?.question||""}),e("div",{className:"flex flex-wrap gap-2",children:i.map(e=>{const s="string"==typeof e?e:e?.id,o="string"==typeof e?e:e?.text||s,i=a===s,c=E&&s===n,l=E&&i&&s!==n;return r("button",{disabled:E,onClick:()=>{const e={...B,[t]:s};K(e),O(e),S?.(e)},className:["rounded-md border px-3 py-1.5 text-sm transition-colors",c?"border-green-500 bg-green-50 text-green-800 font-semibold":"",l?"border-red-500 bg-red-50 text-red-800":"",i&&!E?"border-blue-500 bg-blue-50 text-blue-800":"",i||E?"":"border-gray-200 hover:border-gray-400"].filter(Boolean).join(" "),children:[o,c&&" ✓",l&&" ✗"]},s)})}),E&&r("p",{className:"text-xs text-gray-500",children:["Đáp án: ",e("strong",{children:i.find(e=>(e?.id??e)===n)?.text??n})]})]},t)})})})();case"FILL_IN_BLANK":return V();case"TRUE_FALSE":return(()=>{const t=R.answer,{correctAnswer:n}=t;return r("div",{className:"space-y-3",children:[[{value:!0,label:"Đúng"},{value:!1,label:"Sai"}].map(s=>{const t=T===s.value;return e("div",{className:`flex cursor-pointer items-center rounded-lg border p-3 transition-colors ${t?"border-blue-500 bg-blue-50":"border-gray-200 hover:border-gray-300"} ${E&&s.value===n?"border-green-500 bg-green-50":E&&t&&s.value!==n?"border-red-500 bg-red-50":""}`,onClick:()=>!E&&O(s.value),children:r("div",{className:"flex w-full items-center",children:[e("div",{className:`mr-3 h-4 w-4 rounded-full border-2 ${t?"border-blue-500 bg-blue-500":"border-gray-300"} ${E&&s.value===n?"border-green-500 bg-green-500":""}`,children:t&&e("div",{className:"m-auto h-2 w-2 rounded-full bg-white"})}),e("span",{className:"flex-1",children:s.label}),E&&s.value===n&&e(c,{className:"ml-2 h-4 w-4 text-green-600"}),E&&t&&s.value!==n&&e(d,{className:"ml-2 h-4 w-4 text-red-600"})]})},s.value.toString())}),!E&&e(s,{onClick:()=>{S&&S(T)},disabled:null===T,children:"Nộp đáp án"})]})})();case"ESSAY":return(()=>{const t=R.answer,{minWords:n,maxWords:o}=t,a=T?T.trim().split(/\s+/).length:0;return r("div",{className:"space-y-4",children:[r("div",{className:"text-sm text-gray-600",children:[n&&o?`Số từ: ${n} - ${o} từ`:n?`Tối thiểu: ${n} từ`:o?`Tối đa: ${o} từ`:"",T&&r("span",{className:"ml-2 "+(a>=(n||0)&&a<=(o||1/0)?"text-green-600":"text-red-600"),children:["(",a," từ)"]})]}),e("textarea",{value:T||"",onChange:e=>O(e.target.value),placeholder:"Viết câu trả lời của bạn ở đây...",className:"h-32 w-full resize-none rounded-lg border border-gray-300 p-3 focus:border-blue-500 focus:ring-1 focus:ring-blue-500",disabled:E}),!E&&e(s,{onClick:()=>{S&&S(T)},disabled:!T||T.trim().length<(n||0),children:"Nộp đáp án"})]})})();case"LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE":return(()=>{const t=R.answer,{audioUrl:n,blanks:o}=t;return r("div",{className:"space-y-4",children:[r("div",{className:"flex items-center gap-4 rounded-lg bg-blue-50 p-4",children:[r(s,{onClick:Y,variant:"outline",className:"gap-2",children:[e(l,{className:"h-5 w-5"}),"Phát âm thanh (",I,"/",t.maxPlayCount||3,")"]}),e("audio",{ref:k,src:n})]}),e("div",{className:"space-y-3",children:o.map((s,t)=>r("div",{className:"flex items-center gap-3",children:[r("span",{className:"w-20 font-medium",children:["Blank ",t+1,":"]}),e(i,{value:U[t],onChange:e=>{const r=[...U];r[t]=e.target.value,Q(r),O(r)},placeholder:"Nhập câu trả lời...",disabled:E,className:E?U[t]?.toLowerCase()===o[t]?.toLowerCase()?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""}),E&&r("span",{className:"text-sm text-gray-600",children:["Đáp án: ",e("strong",{children:o[t]})]})]},t))})]})})();case"TICK_TRUE_OR_FALSE":case"TICK_YES_OR_NO":return(()=>{const s=R.answer,{statements:t}=s;return e("div",{className:"space-y-3",children:t.map(s=>e("div",{className:`rounded-lg border-2 p-4 ${E&&q[s.id]===s.isCorrect?"border-green-500 bg-green-50":""} ${E&&q[s.id]!==s.isCorrect?"border-red-500 bg-red-50":"border-gray-200"} `,children:r("div",{className:"flex items-start gap-4",children:[r("div",{className:"mt-1 flex gap-2",children:[e("button",{onClick:()=>{const e={...q,[s.id]:!0};D(e),O(e)},disabled:E,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!0===q[s.id]?"border-green-500 bg-green-500 text-white":"border-gray-300"} `,children:e(c,{className:"h-5 w-5"})}),e("button",{onClick:()=>{const e={...q,[s.id]:!1};D(e),O(e)},disabled:E,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!1===q[s.id]?"border-red-500 bg-red-500 text-white":"border-gray-300"} `,children:e(d,{className:"h-5 w-5"})})]}),e("p",{className:"flex-1",children:s.text}),E&&e("span",{className:"text-sm font-medium",children:s.isCorrect?"✓ Đúng":"✗ Sai"})]})},s.id))})})();case"ARRANGE_LETTERS_INTO_WORDS":return(()=>{const r=R.answer,s=R.correctAnswer||R.correct_answer,t=(r?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctWord||"";return{...e,correctWord:r}});return e(h,{questionData:{subQuestions:t},onSubmit:e=>{O(e),S?.(e)},isReviewMode:E,userAnswer:L})})();case"READ_PASSAGE_AND_COMPLETE_STORY":return(()=>{const s=R.answer,{passage:t,blanks:n}=s,o=t.split(/\{(\d+)\}/);return e("div",{className:"space-y-4",children:e("div",{className:"prose max-w-none",children:e("div",{className:"text-lg leading-relaxed",children:o.map((s,t)=>{const o=parseInt(s);if(!isNaN(o)){const s=n.find(e=>e.index===o);return r("span",{className:"mx-1 inline-flex items-center",children:[e(i,{value:H[o]||"",onChange:e=>{const r={...H,[o]:e.target.value};$(r),O(r)},className:`inline-block h-8 w-32 text-center ${E&&H[o]===s?.correctAnswer?"border-green-500 bg-green-50":""} ${E&&H[o]!==s?.correctAnswer?"border-red-500 bg-red-50":""} `,disabled:E}),E&&r("span",{className:"ml-2 text-sm text-green-600",children:["(",s?.correctAnswer,")"]})]},t)}return e("span",{children:s},t)})})})})})();case"WRITE_SHORT_PARAGRAPH":return(()=>{const r=R.answer;return e(w,{questionData:{contexts:r?.contexts||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{O(e),S?.(e)},isReviewMode:E,userAnswer:L})})();case"WRITE_A_SHORT_LETTER":return(()=>{const r=R.answer;return e(A,{questionData:{contexts:r?.contexts||[],informations:r?.informations||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{O(e),S?.(e)},isReviewMode:E,userAnswer:L})})();case"LOOK_PICTURE_FILL_WORD_HINT":return(()=>{const r=R.answer,s=R.content,t=R.questionGroup;let n=[];r?.subQuestions&&r.subQuestions.length>0?n=r.subQuestions:s?.imageUrl&&s?.word&&(n=[{id:R.id,imageUrl:s.imageUrl,word:s.word,correctAnswer:r?.correctAnswer||"",groupNumber:t?.groupNumber||1}]);const o=t?.payload?.hintLetters||r?.hintLetters||s?.hintLetters||[],a=t?.groupNumber||1,i={};o.length>0&&(i[a]=o);const c={subQuestions:n,hintLetters:o,hintLettersByGroup:i,groups:[{number:a,questionCount:n.length}]};return e(x,{questionData:c,userAnswer:L,isReviewMode:E,explanation:R.explanation,onSubmit:e=>{O(e),S?.(e)}})})();case"LABEL_THE_PICTURE":return(()=>{const r=R.answer;return e(N,{questionData:r,userAnswer:L,isReviewMode:E,onSubmit:e=>{O(e),S?.(e)}})})();case"READ_AND_COLOR_OBJECTS":return(()=>{const r=R.answer;return e(b,{questionData:{backgroundImageUrl:r?.backgroundImageUrl||"",colorNodes:r?.colorNodes||[],regionNodes:r?.regionNodes||[],instructions:r?.instructions||[]},onSubmit:e=>{O(e),S?.(e)}})})();case"READ_PASSAGE_AND_ANSWER_QUESTIONS":return(()=>{const r=R.answer;return e(f,{questionData:{passage:r?.passage||"",passageTitle:r?.passageTitle||"",instructions:r?.instructions||"",subQuestions:r?.subQuestions||[]},userAnswer:L,isReviewMode:E,onSubmit:e=>{O(e),S?.(e)}})})();case"LOOK_PICTURE_CHOOSE_CORRECT_ANSWER":return(()=>{const r=R.content,s=R.answer,t=(r?.subQuestions||s?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctAnswer;return{...e,options:(e.options||[]).map(e=>({...e,isCorrect:e.id===r}))}});return e(_,{questionData:{subQuestions:t},userAnswer:L,isReviewMode:E,onSubmit:e=>{O(e),S?.(e)}})})();case"LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER":return(()=>{const r=R.content,s=R.answer,t=(r?.subQuestions||s?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctAnswer;return{...e,options:(e.options||[]).map(e=>({...e,isCorrect:e.id===r}))}});return e(y,{questionData:{subQuestions:t},userAnswer:L,isReviewMode:E,onSubmit:e=>{O(e),S?.(e)}})})();case"FILL_MISSING_WORDS_IN_GRID":return(()=>{const r=R.content,s=R.answer;return e(v,{questionData:{content:{grid:r?.grid||[],rows:r?.rows||3,columns:r?.columns||3,disabledCells:r?.disabledCells||[]},answer:{answers:s?.answers||{},missingLetters:s?.missingLetters||[],caseSensitive:s?.caseSensitive||!1}},userAnswer:L,isReviewMode:E,onSubmit:e=>{O(e),S?.(e)}})})();default:return r("div",{children:["Loại câu hỏi không được hỗ trợ: ",R.type]})}})(),!E&&e("div",{className:"flex justify-end border-t pt-4",children:e(s,{onClick:()=>{S?.(T)},className:"bg-blue-500 hover:bg-blue-600",children:"Xác nhận đáp án"})})]})]})}
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Button as s}from"../../../components/ui/button";import{Card as t,CardContent as n,CardHeader as o,CardTitle as i}from"../../../components/ui/card";import{Input as a}from"../../../components/ui/input";import{Check as c,Volume2 as l,X as u}from"lucide-react";import{useRef as d,useState as m}from"react";import{toast as p}from"sonner";import{t as g}from"../../../shared/lib/i18n";import{ReadAndColorObjectsClient as b}from"../types/read-and-color-objects/ReadAndColorObjectsClient";import{ArrangeLettersIntoWordsClient as w}from"../types/arrange-letters-into-words/ArrangeLettersIntoWordsClient";import{WriteShortParagraphClient as h}from"../types/write-short-paragraph/WriteShortParagraphClient";import{LabelThePictureClient as N}from"../types/label-the-picture/LabelThePictureClient";import{LookPictureFillWordHintClient as _}from"../types/look-picture-fill-word-hint/LookPictureFillWordHintClient";import{ReadPassageAndAnswerQuestionsClient as C}from"../types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient";import{ChooseTheCorrectAnswerClient as A}from"../types/choose-the-correct-answer/ChooseTheCorrectAnswerClient";import{WriteAShortLetterClient as R}from"../types/write-a-short-letter/WriteAShortLetterClient";import{LookPictureChooseCorrectAnswerClient as f}from"../types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient";import{LookPictureFillBlankChooseAnswerClient as v}from"../types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient";import{FillMissingWordsInGridClient as x}from"../types/fill-missing-words-in-grid/FillMissingWordsInGridClient";import{WordFillStructuredFormViewer as S}from"./WordFillStructuredFormViewer";import{ChooseTheCorrectAnswerGroupViewer as y}from"./ChooseTheCorrectAnswerGroupViewer";import{TrueFalseGroupViewer as E}from"./TrueFalseGroupViewer";import{CrossOutWordGroupViewer as O}from"./CrossOutWordGroupViewer";import{SpontaneousQaGroupViewer as T}from"./SpontaneousQaGroupViewer";import{WordOrderAndMatchGroupViewer as L}from"./WordOrderAndMatchGroupViewer";export function QuestionViewer({question:W,onSubmitAnswer:I,isReviewMode:D=!1,userAnswer:q}){const[U,P]=m(q||null),[k,G]=m(0),M=d(null),[Q,F]=m(q||{}),H=W.answer,[$,j]=m(q||Array(H?.blanks?.length||0).fill("")),[B,K]=m(q||{}),[V,Y]=m(q||{}),[z,J]=m(q||{}),X=()=>{const e=W.answer.maxPlayCount||3;k>=e?p.warning(g("common.audio.maxPlayReached",{maxPlay:String(e)})):(M.current?.play(),G(k+1))},Z=()=>{const t=W.answer,{blanks:n,caseSensitive:o=!1}=t;return r("div",{className:"space-y-4",children:[n.map((s,t)=>{const i=$[t]||"",l=D&&((e,r)=>{const s=n[e];return o?r===s:r.toLowerCase()===s.toLowerCase()})(t,i);return r("div",{className:"flex items-center space-x-2",children:[r("span",{className:"text-sm font-medium",children:["Chỗ trống ",t+1,":"]}),e(a,{value:i,onChange:e=>((e,r)=>{const s=[...$];s[e]=r,j(s)})(t,e.target.value),placeholder:"Nhập đáp án...",className:"flex-1 "+(D?l?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""),disabled:D}),D&&l&&e(c,{className:"h-4 w-4 text-green-600"}),D&&!l&&i&&e(u,{className:"h-4 w-4 text-red-600"})]},t)}),!D&&e(s,{onClick:()=>{I&&I($)},disabled:$.some(e=>""===e.trim()),children:"Nộp đáp án"})]})};return r(t,{children:[e(o,{children:r(i,{className:"flex items-center justify-between",children:[e("span",{children:"string"==typeof W.content?W.content:W.content?.question||W.content?.title||"Câu hỏi"}),r("span",{className:"text-sm font-normal text-gray-500",children:[W.points," điểm"]})]})}),r(n,{className:"space-y-4",children:[(()=>{switch(W.type){case"CHOOSE_THE_CORRECT_ANSWER":return(()=>{const r=W.answer,{isGroup:s,questionGroup:t}=W;let n={};return n=W.content&&"object"==typeof W.content?{...W.content,correctAnswer:W.correctAnswer?.answer??-1,id:W.id,isGroup:s,groupTitle:t?.payload?.title,groupContent:t?.payload?.content,groupImageUrl:t?.payload?.imageUrl,groupNumber:t?.groupNumber}:{...r,question:W.content||r?.question,id:W.id,isGroup:s,groupTitle:t?.payload?.title,groupContent:t?.payload?.content,groupImageUrl:t?.payload?.imageUrl,groupNumber:t?.groupNumber},e(A,{questionData:n,questionConfig:W.questionConfig,onSubmit:e=>{P(e),I?.(e)},isReviewMode:D,userAnswer:q})})();case"CHOOSE_THE_CORRECT_ANSWER_GROUP":return e(y,{question:W,isReviewMode:D,userAnswer:q});case"TRUE_FALSE_GROUP":return e(E,{question:W,isReviewMode:D,userAnswer:q});case"CROSS_OUT_WORD_GROUP":return e(O,{question:W,isReviewMode:D});case"SPONTANEOUS_QA_GROUP":return e(T,{question:W});case"WORD_ORDER_AND_MATCH_GROUP":return e(L,{question:W});case"FILL_IN_BLANK":return Z();case"TRUE_FALSE":return(()=>{const t=W.answer,{correctAnswer:n}=t;return r("div",{className:"space-y-3",children:[[{value:!0,label:"Đúng"},{value:!1,label:"Sai"}].map(s=>{const t=U===s.value;return e("div",{className:`flex cursor-pointer items-center rounded-lg border p-3 transition-colors ${t?"border-blue-500 bg-blue-50":"border-gray-200 hover:border-gray-300"} ${D&&s.value===n?"border-green-500 bg-green-50":D&&t&&s.value!==n?"border-red-500 bg-red-50":""}`,onClick:()=>!D&&P(s.value),children:r("div",{className:"flex w-full items-center",children:[e("div",{className:`mr-3 h-4 w-4 rounded-full border-2 ${t?"border-blue-500 bg-blue-500":"border-gray-300"} ${D&&s.value===n?"border-green-500 bg-green-500":""}`,children:t&&e("div",{className:"m-auto h-2 w-2 rounded-full bg-white"})}),e("span",{className:"flex-1",children:s.label}),D&&s.value===n&&e(c,{className:"ml-2 h-4 w-4 text-green-600"}),D&&t&&s.value!==n&&e(u,{className:"ml-2 h-4 w-4 text-red-600"})]})},s.value.toString())}),!D&&e(s,{onClick:()=>{I&&I(U)},disabled:null===U,children:"Nộp đáp án"})]})})();case"ESSAY":return(()=>{const t=W.answer,{minWords:n,maxWords:o}=t,i=U?U.trim().split(/\s+/).length:0;return r("div",{className:"space-y-4",children:[r("div",{className:"text-sm text-gray-600",children:[n&&o?`Số từ: ${n} - ${o} từ`:n?`Tối thiểu: ${n} từ`:o?`Tối đa: ${o} từ`:"",U&&r("span",{className:"ml-2 "+(i>=(n||0)&&i<=(o||1/0)?"text-green-600":"text-red-600"),children:["(",i," từ)"]})]}),e("textarea",{value:U||"",onChange:e=>P(e.target.value),placeholder:"Viết câu trả lời của bạn ở đây...",className:"h-32 w-full resize-none rounded-lg border border-gray-300 p-3 focus:border-blue-500 focus:ring-1 focus:ring-blue-500",disabled:D}),!D&&e(s,{onClick:()=>{I&&I(U)},disabled:!U||U.trim().length<(n||0),children:"Nộp đáp án"})]})})();case"LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE":return(()=>{const t=W.answer,{audioUrl:n,blanks:o}=t;return r("div",{className:"space-y-4",children:[r("div",{className:"flex items-center gap-4 rounded-lg bg-blue-50 p-4",children:[r(s,{onClick:X,variant:"outline",className:"gap-2",children:[e(l,{className:"h-5 w-5"}),"Phát âm thanh (",k,"/",t.maxPlayCount||3,")"]}),e("audio",{ref:M,src:n})]}),e("div",{className:"space-y-3",children:o.map((s,t)=>r("div",{className:"flex items-center gap-3",children:[r("span",{className:"w-20 font-medium",children:["Blank ",t+1,":"]}),e(a,{value:$[t],onChange:e=>{const r=[...$];r[t]=e.target.value,j(r),P(r)},placeholder:"Nhập câu trả lời...",disabled:D,className:D?$[t]?.toLowerCase()===o[t]?.toLowerCase()?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""}),D&&r("span",{className:"text-sm text-gray-600",children:["Đáp án: ",e("strong",{children:o[t]})]})]},t))})]})})();case"TICK_TRUE_OR_FALSE":case"TICK_YES_OR_NO":return(()=>{const s=W.answer,{statements:t}=s;return e("div",{className:"space-y-3",children:t.map(s=>e("div",{className:`rounded-lg border-2 p-4 ${D&&Q[s.id]===s.isCorrect?"border-green-500 bg-green-50":""} ${D&&Q[s.id]!==s.isCorrect?"border-red-500 bg-red-50":"border-gray-200"} `,children:r("div",{className:"flex items-start gap-4",children:[r("div",{className:"mt-1 flex gap-2",children:[e("button",{onClick:()=>{const e={...Q,[s.id]:!0};F(e),P(e)},disabled:D,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!0===Q[s.id]?"border-green-500 bg-green-500 text-white":"border-gray-300"} `,children:e(c,{className:"h-5 w-5"})}),e("button",{onClick:()=>{const e={...Q,[s.id]:!1};F(e),P(e)},disabled:D,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!1===Q[s.id]?"border-red-500 bg-red-500 text-white":"border-gray-300"} `,children:e(u,{className:"h-5 w-5"})})]}),e("p",{className:"flex-1",children:s.text}),D&&e("span",{className:"text-sm font-medium",children:s.isCorrect?"✓ Đúng":"✗ Sai"})]})},s.id))})})();case"ARRANGE_LETTERS_INTO_WORDS":return(()=>{const r=W.answer,s=W.correctAnswer||W.correct_answer,t=(r?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctWord||"";return{...e,correctWord:r}});return e(w,{questionData:{subQuestions:t},onSubmit:e=>{P(e),I?.(e)},isReviewMode:D,userAnswer:q})})();case"READ_PASSAGE_AND_COMPLETE_STORY":return(()=>{const s=W.answer,{passage:t,blanks:n}=s,o=t.split(/\{(\d+)\}/);return e("div",{className:"space-y-4",children:e("div",{className:"prose max-w-none",children:e("div",{className:"text-lg leading-relaxed",children:o.map((s,t)=>{const o=parseInt(s);if(!isNaN(o)){const s=n.find(e=>e.index===o);return r("span",{className:"mx-1 inline-flex items-center",children:[e(a,{value:V[o]||"",onChange:e=>{const r={...V,[o]:e.target.value};Y(r),P(r)},className:`inline-block h-8 w-32 text-center ${D&&V[o]===s?.correctAnswer?"border-green-500 bg-green-50":""} ${D&&V[o]!==s?.correctAnswer?"border-red-500 bg-red-50":""} `,disabled:D}),D&&r("span",{className:"ml-2 text-sm text-green-600",children:["(",s?.correctAnswer,")"]})]},t)}return e("span",{children:s},t)})})})})})();case"WRITE_SHORT_PARAGRAPH":return(()=>{const r=W.answer;return e(h,{questionData:{contexts:r?.contexts||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{P(e),I?.(e)},isReviewMode:D,userAnswer:q})})();case"WRITE_A_SHORT_LETTER":return(()=>{const r=W.answer;return e(R,{questionData:{contexts:r?.contexts||[],informations:r?.informations||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{P(e),I?.(e)},isReviewMode:D,userAnswer:q})})();case"LOOK_PICTURE_FILL_WORD_HINT":return(()=>{const r=W.answer,s=W.content,t=W.questionGroup;let n=[];r?.subQuestions&&r.subQuestions.length>0?n=r.subQuestions:s?.imageUrl&&s?.word&&(n=[{id:W.id,imageUrl:s.imageUrl,word:s.word,correctAnswer:r?.correctAnswer||"",groupNumber:t?.groupNumber||1}]);const o=t?.payload?.hintLetters||r?.hintLetters||s?.hintLetters||[],i=t?.groupNumber||1,a={};o.length>0&&(a[i]=o);const c={subQuestions:n,hintLetters:o,hintLettersByGroup:a,groups:[{number:i,questionCount:n.length}]};return e(_,{questionData:c,userAnswer:q,isReviewMode:D,explanation:W.explanation,onSubmit:e=>{P(e),I?.(e)}})})();case"LABEL_THE_PICTURE":return(()=>{const r=W.answer;return e(N,{questionData:r,userAnswer:q,isReviewMode:D,onSubmit:e=>{P(e),I?.(e)}})})();case"READ_AND_COLOR_OBJECTS":return(()=>{const r=W.answer;return e(b,{questionData:{backgroundImageUrl:r?.backgroundImageUrl||"",colorNodes:r?.colorNodes||[],regionNodes:r?.regionNodes||[],instructions:r?.instructions||[]},onSubmit:e=>{P(e),I?.(e)}})})();case"READ_PASSAGE_AND_ANSWER_QUESTIONS":return(()=>{const r=W.answer;return e(C,{questionData:{passage:r?.passage||"",passageTitle:r?.passageTitle||"",instructions:r?.instructions||"",subQuestions:r?.subQuestions||[]},userAnswer:q,isReviewMode:D,onSubmit:e=>{P(e),I?.(e)}})})();case"LOOK_PICTURE_CHOOSE_CORRECT_ANSWER":return(()=>{const r=W.content,s=W.answer,t=(r?.subQuestions||s?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctAnswer;return{...e,options:(e.options||[]).map(e=>({...e,isCorrect:e.id===r}))}});return e(f,{questionData:{subQuestions:t},userAnswer:q,isReviewMode:D,onSubmit:e=>{P(e),I?.(e)}})})();case"LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER":return(()=>{const r=W.content,s=W.answer,t=(r?.subQuestions||s?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctAnswer;return{...e,options:(e.options||[]).map(e=>({...e,isCorrect:e.id===r}))}});return e(v,{questionData:{subQuestions:t},userAnswer:q,isReviewMode:D,onSubmit:e=>{P(e),I?.(e)}})})();case"FILL_MISSING_WORDS_IN_GRID":return(()=>{const r=W.content,s=W.answer;return e(x,{questionData:{content:{grid:r?.grid||[],rows:r?.rows||3,columns:r?.columns||3,disabledCells:r?.disabledCells||[]},answer:{answers:s?.answers||{},missingLetters:s?.missingLetters||[],caseSensitive:s?.caseSensitive||!1}},userAnswer:q,isReviewMode:D,onSubmit:e=>{P(e),I?.(e)}})})();case"WORD_FILL_STRUCTURED_FORM":return e(S,{question:W,isReviewMode:D,userAnswer:q,onSubmitAnswer:I});default:return r("div",{children:["Loại câu hỏi không được hỗ trợ: ",W.type]})}})(),!D&&e("div",{className:"flex justify-end border-t pt-4",children:e(s,{onClick:()=>{I?.(U)},className:"bg-blue-500 hover:bg-blue-600",children:"Xác nhận đáp án"})})]})]})}
@@ -0,0 +1,4 @@
1
+ import type { Question } from '../../../shared/types/entities/question.types';
2
+ export declare function SpontaneousQaGroupViewer({ question }: {
3
+ question: Question;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";function r(e){return function(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e)?e:{}}function n(e,t=""){return"string"==typeof e?e:t}function s(e,t){const s=r(e),i=r(s.content),o=r(s.correctAnswer),a=Array.isArray(o.expectedAnswers)?o.expectedAnswers.map(e=>String(e??"")):[];return{questionText:n(i.questionText)||n(i.question)||n(s.questionText),audioUrl:n(i.audioUrl)||void 0,expectedAnswers:a,isExample:!0===s.isExample,points:"number"==typeof s.points?s.points:1,questionNumber:"number"==typeof s.questionNumber?s.questionNumber:t+1}}export function SpontaneousQaGroupViewer({question:i}){const{instruction:o,items:a}=function(e){const t=r(e.content),i=r(e.spontaneousQAList??t.spontaneousQAList??t),o=Array.isArray(i.items)?i.items:Array.isArray(t.items)?t.items:[];return{instruction:n(r(i.meta).instruction)||n(r(t.meta).instruction)||n(e.explanation)||"Talk about the topic.",items:o.map(s)}}(i);return t("div",{className:"space-y-4",children:[o?e("div",{className:"rounded-xl border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm font-medium text-indigo-800",children:o}):null,a.map((r,n)=>t("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[t("div",{className:"flex items-center justify-between border-b border-gray-100 bg-gray-50 px-4 py-2.5",children:[t("span",{className:"text-sm font-medium text-gray-700",children:["Câu ",r.questionNumber||n+1]}),t("span",{className:"text-xs text-gray-500",children:[r.points||0," điểm"]})]}),t("div",{className:"space-y-2 p-4",children:[e("p",{className:"text-sm font-semibold text-gray-800",children:r.questionText||"Câu hỏi chưa được nhập"}),r.audioUrl?t("p",{className:"text-xs text-gray-500",children:["Audio: ",r.audioUrl]}):null,r.expectedAnswers.length>0?t("div",{className:"rounded-lg border border-emerald-100 bg-emerald-50 px-3 py-2 text-sm text-emerald-800",children:[e("p",{className:"mb-1 font-medium",children:"Gợi ý đáp án"}),e("ul",{className:"list-disc space-y-1 pl-4",children:r.expectedAnswers.map(t=>e("li",{children:t},t))})]}):null]})]},`${r.questionNumber}-${n}`))]})}
@@ -0,0 +1,8 @@
1
+ import type { Question } from '../../../shared/types/entities/question.types';
2
+ type TrueFalseGroupViewerProps = {
3
+ question: Question;
4
+ isReviewMode?: boolean;
5
+ userAnswer?: boolean[];
6
+ };
7
+ export declare function TrueFalseGroupViewer({ question, isReviewMode, userAnswer, }: TrueFalseGroupViewerProps): import("react").JSX.Element;
8
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e}from"react/jsx-runtime";import{TrueFalseGroupClient as r}from"../types/true-false-group/TrueFalseGroupClient";import{mapQuestionToTrueFalseGroupData as s}from"../types/true-false-group/map-true-false-group-data";export function TrueFalseGroupViewer({question:t,isReviewMode:o=!1,userAnswer:u}){const i=s(t),a=o&&!u?i.items.map(e=>e.correctAnswer):u;return e(r,{questionData:i,isReviewMode:o,userAnswers:a})}
@@ -0,0 +1,9 @@
1
+ import type { Question } from '../../../shared/types/entities/question.types';
2
+ type WordFillStructuredFormViewerProps = {
3
+ question: Question;
4
+ isReviewMode?: boolean;
5
+ userAnswer?: Record<string, string>;
6
+ onSubmitAnswer?: (answer: Record<string, string>) => void;
7
+ };
8
+ export declare function WordFillStructuredFormViewer({ question, isReviewMode, userAnswer, onSubmitAnswer, }: WordFillStructuredFormViewerProps): import("react").JSX.Element;
9
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as r}from"react/jsx-runtime";import{useState as e}from"react";import{WordFillStructuredFormClient as t}from"../types/word-fill-structured-form/WordFillStructuredFormClient";import{mapQuestionToWfsfData as o}from"../types/word-fill-structured-form/map-wfsf-question-data";export function WordFillStructuredFormViewer({question:i,isReviewMode:n=!1,userAnswer:s,onSubmitAnswer:u}){const m=o(i),[a,l]=e(()=>s??{});return r(t,{questionData:m,isReviewMode:n,userAnswer:s,autoFillCorrectAnswers:n&&!s,instruction:m.instruction,questionNumber:i.questionNumber,explanation:i.explanation,onAnswerChange:(r,e)=>{const t={...a,[r]:e};l(t),u?.(t)}})}
@@ -0,0 +1,4 @@
1
+ import type { Question } from '../../../shared/types/entities/question.types';
2
+ export declare function WordOrderAndMatchGroupViewer({ question, }: {
3
+ question: Question;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";function t(e){return function(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}(e)?e:{}}function s(e,r=""){return"string"==typeof e?e:r}function a(e,r){const a=t(e),n=t(a.content),i=t(a.correctAnswer);return{scrambledWords:Array.isArray(n.scrambledWords)?n.scrambledWords.map(e=>String(e??"")):[],orderedPhrase:s(i.orderedPhrase),matchLabel:s(i.matchLabel),isExample:!0===a.isExample,points:"number"==typeof a.points?a.points:1,questionNumber:"number"==typeof a.questionNumber?a.questionNumber:r+1}}export function WordOrderAndMatchGroupViewer({question:n}){const{instruction:i,matchOptions:d,items:o}=function(e){const r=t(e.content),n=t(e.wordOrderMatchList??r.wordOrderMatchList??r),i=t(n.meta??r.meta),d=Array.isArray(n.items)?n.items:Array.isArray(r.items)?r.items:[];return{instruction:s(i.instruction)||s(e.explanation)||"Put the phrases in order, then match the labels.",matchOptions:Array.isArray(i.matchOptions)?i.matchOptions.map(e=>String(e??"")):[],items:d.map(a)}}(n);return r("div",{className:"space-y-4",children:[i?e("div",{className:"rounded-xl border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm font-medium text-indigo-800",children:i}):null,d.length>0?e("div",{className:"flex flex-wrap gap-2",children:d.map(r=>e("span",{className:"rounded-md border border-gray-200 bg-white px-2 py-1 text-xs font-semibold text-gray-700",children:r},r))}):null,o.map((t,s)=>r("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:[r("div",{className:"flex items-center justify-between border-b border-gray-100 bg-gray-50 px-4 py-2.5",children:[r("span",{className:"text-sm font-medium text-gray-700",children:["Câu ",t.questionNumber||s+1,t.isExample?" · Example":""]}),r("span",{className:"text-xs text-gray-500",children:[t.points||0," điểm"]})]}),r("div",{className:"space-y-2 p-4",children:[e("div",{className:"flex flex-wrap gap-1.5",children:t.scrambledWords.map((r,t)=>e("span",{className:"rounded-md border border-dashed border-teal-300 bg-teal-50 px-2 py-1 text-sm text-teal-800",children:r},`${r}-${t}`))}),r("p",{className:"text-sm text-gray-800",children:[e("span",{className:"font-medium text-gray-500",children:"Câu đúng: "}),t.orderedPhrase||"—"]}),r("p",{className:"text-sm text-gray-800",children:[e("span",{className:"font-medium text-gray-500",children:"Nhãn: "}),t.matchLabel||"—"]})]})]},`${t.questionNumber}-${s}`))]})}
@@ -1,2 +1,8 @@
1
1
  export { QuestionViewer } from './QuestionViewer';
2
2
  export { QuestionGroupViewer } from './QuestionGroupViewer';
3
+ export { WordFillStructuredFormViewer } from './WordFillStructuredFormViewer';
4
+ export { ChooseTheCorrectAnswerGroupViewer } from './ChooseTheCorrectAnswerGroupViewer';
5
+ export { TrueFalseGroupViewer } from './TrueFalseGroupViewer';
6
+ export { CrossOutWordGroupViewer } from './CrossOutWordGroupViewer';
7
+ export { SpontaneousQaGroupViewer } from './SpontaneousQaGroupViewer';
8
+ export { WordOrderAndMatchGroupViewer } from './WordOrderAndMatchGroupViewer';
@@ -1 +1 @@
1
- export{QuestionViewer}from"./QuestionViewer";export{QuestionGroupViewer}from"./QuestionGroupViewer";
1
+ export{QuestionViewer}from"./QuestionViewer";export{QuestionGroupViewer}from"./QuestionGroupViewer";export{WordFillStructuredFormViewer}from"./WordFillStructuredFormViewer";export{ChooseTheCorrectAnswerGroupViewer}from"./ChooseTheCorrectAnswerGroupViewer";export{TrueFalseGroupViewer}from"./TrueFalseGroupViewer";export{CrossOutWordGroupViewer}from"./CrossOutWordGroupViewer";export{SpontaneousQaGroupViewer}from"./SpontaneousQaGroupViewer";export{WordOrderAndMatchGroupViewer}from"./WordOrderAndMatchGroupViewer";
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export * as Api from './api';
2
2
  export * as Components from './components';
3
- export * as Mcp from './mcp';
4
3
  export * as Exams from './components/exams';
5
4
  export * as Questions from './components/questions';
6
5
  export * as Results from './components/results';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export*as Api from"./api";export*as Components from"./components";export*as Mcp from"./mcp";export*as Exams from"./components/exams";export*as Questions from"./components/questions";export*as Results from"./components/results";export*as Themes from"./components/themes";export*as Shared from"./shared";export{buildExamTemplatePayload,createContestRoomsApi,createExamCreateApi,createExamEntryApi,createExamQuestionsApi,createExamQuestionsFetchApi,createExamTakingApi,createMockContestRoomsApi,createMockCreateExamApi,createMockExamEntryApi,createMockExamQuestionsApi,createMockExamTakingApi,createMockResultReviewApi,createResultReviewApi}from"./api";export{CreateExamPageContainer,ExamPreviewDialog,ExamQuestionsPageContainer,ExamTakingPageContainer,ExamViewDialog,StudentContestRoomsPageContainer,StudentExamEntryPageContainer}from"./components/exams";export{ResultReviewPageContainer}from"./components/results";export{QuestionCreator}from"./components/questions/creator/QuestionCreator";export{QuestionGroupCreator}from"./components/questions/creator/QuestionGroupCreator";export{renderDedicatedComponent}from"./components/questions/creator/dedicated-component-router";export{questionTypeRegistry}from"./components/questions/creator/question-type-registry";export{QUESTION_TYPE_CONFIG,QUESTION_TYPES_WITH_DEDICATED_COMPONENTS}from"./components/questions/_shared/config/question-types.config";export{validateQuestion}from"./components/questions/_shared/utils/question-validation";export{buildAnswerPayload}from"./components/questions/_shared/utils/answer-builder";export*from"./components/questions/_shared/types";
1
+ export*as Api from"./api";export*as Components from"./components";export*as Exams from"./components/exams";export*as Questions from"./components/questions";export*as Results from"./components/results";export*as Themes from"./components/themes";export*as Shared from"./shared";export{buildExamTemplatePayload,createContestRoomsApi,createExamCreateApi,createExamEntryApi,createExamQuestionsApi,createExamQuestionsFetchApi,createExamTakingApi,createMockContestRoomsApi,createMockCreateExamApi,createMockExamEntryApi,createMockExamQuestionsApi,createMockExamTakingApi,createMockResultReviewApi,createResultReviewApi}from"./api";export{CreateExamPageContainer,ExamPreviewDialog,ExamQuestionsPageContainer,ExamTakingPageContainer,ExamViewDialog,StudentContestRoomsPageContainer,StudentExamEntryPageContainer}from"./components/exams";export{ResultReviewPageContainer}from"./components/results";export{QuestionCreator}from"./components/questions/creator/QuestionCreator";export{QuestionGroupCreator}from"./components/questions/creator/QuestionGroupCreator";export{renderDedicatedComponent}from"./components/questions/creator/dedicated-component-router";export{questionTypeRegistry}from"./components/questions/creator/question-type-registry";export{QUESTION_TYPE_CONFIG,QUESTION_TYPES_WITH_DEDICATED_COMPONENTS}from"./components/questions/_shared/config/question-types.config";export{validateQuestion}from"./components/questions/_shared/utils/question-validation";export{buildAnswerPayload}from"./components/questions/_shared/utils/answer-builder";export*from"./components/questions/_shared/types";
@@ -1 +1 @@
1
- import{SkillEnum as E}from"../types/common.types";export const QUESTION_TYPE_SKILLS={LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE:E.LISTENING,LISTEN_AND_TICK_ANSWER:E.LISTENING,LISTEN_AND_FILL_NAME_NUMBER:E.LISTENING,LISTENING_FILL_BLANK:E.LISTENING,LISTENING_COLOR:E.LISTENING,LISTENING:E.LISTENING,LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:E.SPEAKING,LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:E.SPEAKING,LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:E.SPEAKING,LISTEN_AND_SPEAK_ANSWER:E.SPEAKING,LISTEN_AND_SPEAK_IMAGE_GROUP:E.SPEAKING,LISTEN_AND_SPEAK_QUESTION_LIST:E.SPEAKING,LISTEN_AND_SPEAK_COMPARE_IMAGES:E.SPEAKING,LISTEN_AND_SPEAK_WITH_STORY_IMAGES:E.SPEAKING,LISTEN_AND_SPEAK_ODD_ONE_OUT:E.SPEAKING,LISTEN_AND_SPEAK_INFO_EXCHANGE:E.SPEAKING,READ_DISPLAYED_CONTENT:E.SPEAKING,SPEAKING_DESCRIBE_IMAGE:E.SPEAKING,SPONTANEOUS_QA:E.SPEAKING,SPEAKING_CONVERSATION:E.SPEAKING,SPEAKING_CUE_CARD:E.SPEAKING,GN_SPEAKING_INTERVIEW:E.SPEAKING,CHOOSE_THE_CORRECT_ANSWER:E.READING,CHOOSE_THE_CORRECT_ANSWER_GROUP:E.READING,CHOOSE_CORRECT_ADJECTIVE:E.READING,FILL_IN_BLANK:E.READING,FILL_MISSING_WORDS_IN_GRID:E.READING,READ_PASSAGE_AND_ANSWER_QUESTIONS:E.READING,ANSWER_THE_QUESTION:E.READING,WORD_ORDERING:E.READING,WORD_FILL_PARAGRAPH:E.READING,MATCH_BY_WRITING_ANSWER:E.READING,LABEL_THE_PICTURE:E.READING,LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:E.READING,LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:E.READING,LOOK_PICTURE_FILL_WORD_HINT:E.READING,READ_AND_COLOR_OBJECTS:E.READING,IMAGE_OBJECT_MATCHING:E.READING,TRUE_FALSE:E.READING,TRUE_FALSE_GROUP:E.READING,CROSS_OUT_WORD_GROUP:E.READING,MATCHING:E.READING,ORDERING:E.READING,TICK_TRUE_OR_FALSE:E.READING,TICK_YES_OR_NO:E.READING,READING_TICK_CROSS:E.READING,ARRANGE_LETTERS_INTO_WORDS:E.READING,READ_PASSAGE_AND_COMPLETE_STORY:E.READING,CLOZE_TEST_WITH_TICK_BOX:E.READING,READ_AND_CHOOSE_APPROPRIATE_WORD:E.READING,READING_LETTER_ARRANGE:E.READING,READING_PASSAGE_FILL:E.READING,FILL_BLANK:E.READING,WORD_FILL_STRUCTURED_FORM:E.READING,WRITE_A_SHORT_LETTER:E.WRITING,WRITE_CORRECT_VERB_FORM:E.WRITING,WRITE_SHORT_PARAGRAPH:E.WRITING,WRITE_SENTENCES:E.WRITING,ESSAY:E.WRITING,COLORING:E.WRITING};export function getQuestionTypeSkill(E){return QUESTION_TYPE_SKILLS[E]||"READING"}export function getQuestionTypesBySkill(E){return Object.entries(QUESTION_TYPE_SKILLS).filter(([_,N])=>N===E).map(([E,_])=>E)}export const QUESTION_TYPES_BY_SKILL={LISTENING:getQuestionTypesBySkill("LISTENING"),SPEAKING:getQuestionTypesBySkill("SPEAKING"),READING:getQuestionTypesBySkill("READING"),WRITING:getQuestionTypesBySkill("WRITING")};export const MARKETING_QUESTION_TYPES_BY_SKILL={[E.LISTENING]:[],[E.SPEAKING]:[],[E.READING]:["CHOOSE_THE_CORRECT_ANSWER","CHOOSE_CORRECT_ADJECTIVE","FILL_IN_BLANK","FILL_MISSING_WORDS_IN_GRID","LABEL_THE_PICTURE","LOOK_PICTURE_CHOOSE_CORRECT_ANSWER","LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER","LOOK_PICTURE_FILL_WORD_HINT","READ_AND_COLOR_OBJECTS","IMAGE_OBJECT_MATCHING","READ_PASSAGE_AND_ANSWER_QUESTIONS"],[E.WRITING]:["WRITE_A_SHORT_LETTER","WRITE_CORRECT_VERB_FORM","WRITE_SENTENCES"]};export function isMarketingQuestionType(E){return Object.values(MARKETING_QUESTION_TYPES_BY_SKILL).flat().includes(E)}export const QUESTION_TYPE_LABELS={LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE:"Nghe - Điền từ có hình",LISTEN_AND_TICK_ANSWER:"Nghe - Đánh dấu",LISTEN_AND_FILL_NAME_NUMBER:"Nghe - Điền tên/số",LISTENING_FILL_BLANK:"Nghe - Điền chỗ trống",LISTENING_COLOR:"Nghe - Tô màu",LISTENING:"Nghe",CHOOSE_THE_CORRECT_ANSWER:"Chọn đáp án đúng",CHOOSE_THE_CORRECT_ANSWER_GROUP:"Chọn đáp án đúng (nhóm)",CHOOSE_CORRECT_ADJECTIVE:"Chọn tính từ",FILL_IN_BLANK:"Điền vào chỗ trống",FILL_MISSING_WORDS_IN_GRID:"Điền từ vào lưới",READ_PASSAGE_AND_ANSWER_QUESTIONS:"Đọc hiểu - Trả lời",LABEL_THE_PICTURE:"Gắn nhãn hình ảnh",LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:"Nhìn ảnh - Chọn đáp án",LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:"Nhìn ảnh - Điền và chọn",LOOK_PICTURE_FILL_WORD_HINT:"Nhìn ảnh - Điền từ gợi ý",READ_AND_COLOR_OBJECTS:"Đọc - Tô màu",IMAGE_OBJECT_MATCHING:"Ghép hình ảnh - Đối tượng",TRUE_FALSE:"Đúng/Sai",TRUE_FALSE_GROUP:"Đúng/Sai (nhóm)",CROSS_OUT_WORD_GROUP:"Gạch từ / sửa từ (nhóm)",MATCHING:"Ghép nối",ORDERING:"Sắp xếp",TICK_TRUE_OR_FALSE:"Đánh dấu Đúng/Sai",TICK_YES_OR_NO:"Đánh dấu Có/Không",READING_TICK_CROSS:"Đọc - Đánh dấu",ARRANGE_LETTERS_INTO_WORDS:"Sắp xếp chữ cái thành từ",READ_PASSAGE_AND_COMPLETE_STORY:"Đọc - Hoàn thành câu chuyện",CLOZE_TEST_WITH_TICK_BOX:"Điền từ - Đánh dấu",READ_AND_CHOOSE_APPROPRIATE_WORD:"Đọc - Chọn từ phù hợp",READING_LETTER_ARRANGE:"Đọc - Sắp xếp chữ cái",READING_PASSAGE_FILL:"Đọc - Điền đoạn văn",FILL_BLANK:"Điền chỗ trống",ANSWER_THE_QUESTION:"Nghe - Đọc câu hỏi và viết đáp án",WORD_ORDERING:"Sắp xếp từ",WORD_FILL_PARAGRAPH:"Điền từ vào đoạn văn",MATCH_BY_WRITING_ANSWER:"Ghép đáp án bằng cách viết",WORD_FILL_STRUCTURED_FORM:"Điền từ vào biểu mẫu có cấu trúc",WRITE_A_SHORT_LETTER:"Viết thư",WRITE_CORRECT_VERB_FORM:"Chia động từ",WRITE_SHORT_PARAGRAPH:"Viết đoạn văn ngắn",WRITE_SENTENCES:"Viết câu",ESSAY:"Viết luận",COLORING:"Tô màu",LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:"Nghe và chọn trong nhóm đáp án",LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:"Nghe và chọn đối tượng trong cảnh",LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:"Nghe và kéo thả đối tượng vào cảnh",LISTEN_AND_SPEAK_ANSWER:"Nghe và trả lời (Speaking)",LISTEN_AND_SPEAK_IMAGE_GROUP:"Nghe, nhìn nhóm ảnh và trả lời (Speaking)",LISTEN_AND_SPEAK_QUESTION_LIST:"Nghe, chọn câu hỏi và trả lời (Speaking)",LISTEN_AND_SPEAK_COMPARE_IMAGES:"Nghe và so sánh ảnh (Speaking)",LISTEN_AND_SPEAK_WITH_STORY_IMAGES:"Nghe, nhìn ảnh truyện và kể lại (Speaking)",LISTEN_AND_SPEAK_ODD_ONE_OUT:"Nghe, chọn hình khác loại và giải thích (Speaking)",LISTEN_AND_SPEAK_INFO_EXCHANGE:"Trao đổi thông tin (Speaking)",READ_DISPLAYED_CONTENT:"Đọc nội dung hiển thị (Speaking)",SPEAKING_DESCRIBE_IMAGE:"Nhìn tranh và mô tả (Speaking Part 2)",SPONTANEOUS_QA:"Hỏi đáp tự phát (Speaking Part 3)",SPEAKING_CONVERSATION:"Hội thoại Speaking (KFS Speaking)",GN_SPEAKING_INTERVIEW:"GN Speaking - Hỏi đáp (Virtual + Real)"};export function getQuestionTypeLabel(E){return QUESTION_TYPE_LABELS[E]||E}export const SKILL_METADATA={[E.LISTENING]:{name:"Nghe",emoji:"🎧",color:"text-blue-600",bgColor:"bg-blue-50",borderColor:"border-blue-200",description:"Kỹ năng nghe hiểu"},[E.SPEAKING]:{name:"Nói",emoji:"🗣️",color:"text-green-600",bgColor:"bg-green-50",borderColor:"border-green-200",description:"Kỹ năng giao tiếp"},[E.READING]:{name:"Đọc",emoji:"📖",color:"text-purple-600",bgColor:"bg-purple-50",borderColor:"border-purple-200",description:"Kỹ năng đọc hiểu"},[E.WRITING]:{name:"Viết",emoji:"✏️",color:"text-orange-600",bgColor:"bg-orange-50",borderColor:"border-orange-200",description:"Kỹ năng viết"}};
1
+ import{SkillEnum as E}from"../types/common.types";export const QUESTION_TYPE_SKILLS={LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE:E.LISTENING,LISTEN_AND_TICK_ANSWER:E.LISTENING,LISTEN_AND_FILL_NAME_NUMBER:E.LISTENING,LISTENING_FILL_BLANK:E.LISTENING,LISTENING_COLOR:E.LISTENING,LISTENING:E.LISTENING,LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:E.SPEAKING,LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:E.SPEAKING,LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:E.SPEAKING,LISTEN_AND_SPEAK_ANSWER:E.SPEAKING,LISTEN_AND_SPEAK_IMAGE_GROUP:E.SPEAKING,LISTEN_AND_SPEAK_QUESTION_LIST:E.SPEAKING,LISTEN_AND_SPEAK_COMPARE_IMAGES:E.SPEAKING,LISTEN_AND_SPEAK_WITH_STORY_IMAGES:E.SPEAKING,LISTEN_AND_SPEAK_ODD_ONE_OUT:E.SPEAKING,LISTEN_AND_SPEAK_INFO_EXCHANGE:E.SPEAKING,READ_DISPLAYED_CONTENT:E.SPEAKING,SPEAKING_DESCRIBE_IMAGE:E.SPEAKING,SPONTANEOUS_QA:E.SPEAKING,SPEAKING_CONVERSATION:E.SPEAKING,SPEAKING_CUE_CARD:E.SPEAKING,GN_SPEAKING_INTERVIEW:E.SPEAKING,CHOOSE_THE_CORRECT_ANSWER:E.READING,CHOOSE_THE_CORRECT_ANSWER_GROUP:E.READING,CHOOSE_CORRECT_ADJECTIVE:E.READING,FILL_IN_BLANK:E.READING,FILL_MISSING_WORDS_IN_GRID:E.READING,READ_PASSAGE_AND_ANSWER_QUESTIONS:E.READING,ANSWER_THE_QUESTION:E.READING,WORD_ORDERING:E.READING,WORD_FILL_PARAGRAPH:E.READING,MATCH_BY_WRITING_ANSWER:E.READING,LABEL_THE_PICTURE:E.READING,LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:E.READING,LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:E.READING,LOOK_PICTURE_FILL_WORD_HINT:E.READING,READ_AND_COLOR_OBJECTS:E.READING,IMAGE_OBJECT_MATCHING:E.READING,TRUE_FALSE:E.READING,TRUE_FALSE_GROUP:E.READING,CROSS_OUT_WORD_GROUP:E.READING,SPONTANEOUS_QA_GROUP:E.SPEAKING,WORD_ORDER_AND_MATCH_GROUP:E.READING,MATCHING:E.READING,ORDERING:E.READING,TICK_TRUE_OR_FALSE:E.READING,TICK_YES_OR_NO:E.READING,READING_TICK_CROSS:E.READING,ARRANGE_LETTERS_INTO_WORDS:E.READING,READ_PASSAGE_AND_COMPLETE_STORY:E.READING,CLOZE_TEST_WITH_TICK_BOX:E.READING,READ_AND_CHOOSE_APPROPRIATE_WORD:E.READING,READING_LETTER_ARRANGE:E.READING,READING_PASSAGE_FILL:E.READING,FILL_BLANK:E.READING,WORD_FILL_STRUCTURED_FORM:E.READING,WRITE_A_SHORT_LETTER:E.WRITING,WRITE_CORRECT_VERB_FORM:E.WRITING,WRITE_SHORT_PARAGRAPH:E.WRITING,WRITE_SENTENCES:E.WRITING,ESSAY:E.WRITING,COLORING:E.WRITING};export function getQuestionTypeSkill(E){return QUESTION_TYPE_SKILLS[E]||"READING"}export function getQuestionTypesBySkill(E){return Object.entries(QUESTION_TYPE_SKILLS).filter(([_,N])=>N===E).map(([E,_])=>E)}export const QUESTION_TYPES_BY_SKILL={LISTENING:getQuestionTypesBySkill("LISTENING"),SPEAKING:getQuestionTypesBySkill("SPEAKING"),READING:getQuestionTypesBySkill("READING"),WRITING:getQuestionTypesBySkill("WRITING")};export const MARKETING_QUESTION_TYPES_BY_SKILL={[E.LISTENING]:[],[E.SPEAKING]:[],[E.READING]:["CHOOSE_THE_CORRECT_ANSWER","CHOOSE_CORRECT_ADJECTIVE","FILL_IN_BLANK","FILL_MISSING_WORDS_IN_GRID","LABEL_THE_PICTURE","LOOK_PICTURE_CHOOSE_CORRECT_ANSWER","LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER","LOOK_PICTURE_FILL_WORD_HINT","READ_AND_COLOR_OBJECTS","IMAGE_OBJECT_MATCHING","READ_PASSAGE_AND_ANSWER_QUESTIONS"],[E.WRITING]:["WRITE_A_SHORT_LETTER","WRITE_CORRECT_VERB_FORM","WRITE_SENTENCES"]};export function isMarketingQuestionType(E){return Object.values(MARKETING_QUESTION_TYPES_BY_SKILL).flat().includes(E)}export const QUESTION_TYPE_LABELS={LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE:"Nghe - Điền từ có hình",LISTEN_AND_TICK_ANSWER:"Nghe - Đánh dấu",LISTEN_AND_FILL_NAME_NUMBER:"Nghe - Điền tên/số",LISTENING_FILL_BLANK:"Nghe - Điền chỗ trống",LISTENING_COLOR:"Nghe - Tô màu",LISTENING:"Nghe",CHOOSE_THE_CORRECT_ANSWER:"Chọn đáp án đúng",CHOOSE_THE_CORRECT_ANSWER_GROUP:"Chọn đáp án đúng (nhóm)",CHOOSE_CORRECT_ADJECTIVE:"Chọn tính từ",FILL_IN_BLANK:"Điền vào chỗ trống",FILL_MISSING_WORDS_IN_GRID:"Điền từ vào lưới",READ_PASSAGE_AND_ANSWER_QUESTIONS:"Đọc hiểu - Trả lời",LABEL_THE_PICTURE:"Gắn nhãn hình ảnh",LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:"Nhìn ảnh - Chọn đáp án",LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:"Nhìn ảnh - Điền và chọn",LOOK_PICTURE_FILL_WORD_HINT:"Nhìn ảnh - Điền từ gợi ý",READ_AND_COLOR_OBJECTS:"Đọc - Tô màu",IMAGE_OBJECT_MATCHING:"Ghép hình ảnh - Đối tượng",TRUE_FALSE:"Đúng/Sai",TRUE_FALSE_GROUP:"Đúng/Sai (nhóm)",CROSS_OUT_WORD_GROUP:"Gạch từ / sửa từ (nhóm)",SPONTANEOUS_QA_GROUP:"Hỏi đáp tự phát (nhóm)",WORD_ORDER_AND_MATCH_GROUP:"Sắp xếp từ và nối nhãn (nhóm)",MATCHING:"Ghép nối",ORDERING:"Sắp xếp",TICK_TRUE_OR_FALSE:"Đánh dấu Đúng/Sai",TICK_YES_OR_NO:"Đánh dấu Có/Không",READING_TICK_CROSS:"Đọc - Đánh dấu",ARRANGE_LETTERS_INTO_WORDS:"Sắp xếp chữ cái thành từ",READ_PASSAGE_AND_COMPLETE_STORY:"Đọc - Hoàn thành câu chuyện",CLOZE_TEST_WITH_TICK_BOX:"Điền từ - Đánh dấu",READ_AND_CHOOSE_APPROPRIATE_WORD:"Đọc - Chọn từ phù hợp",READING_LETTER_ARRANGE:"Đọc - Sắp xếp chữ cái",READING_PASSAGE_FILL:"Đọc - Điền đoạn văn",FILL_BLANK:"Điền chỗ trống",ANSWER_THE_QUESTION:"Nghe - Đọc câu hỏi và viết đáp án",WORD_ORDERING:"Sắp xếp từ",WORD_FILL_PARAGRAPH:"Điền từ vào đoạn văn",MATCH_BY_WRITING_ANSWER:"Ghép đáp án bằng cách viết",WORD_FILL_STRUCTURED_FORM:"Điền từ vào biểu mẫu có cấu trúc",WRITE_A_SHORT_LETTER:"Viết thư",WRITE_CORRECT_VERB_FORM:"Chia động từ",WRITE_SHORT_PARAGRAPH:"Viết đoạn văn ngắn",WRITE_SENTENCES:"Viết câu",ESSAY:"Viết luận",COLORING:"Tô màu",LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:"Nghe và chọn trong nhóm đáp án",LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:"Nghe và chọn đối tượng trong cảnh",LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:"Nghe và kéo thả đối tượng vào cảnh",LISTEN_AND_SPEAK_ANSWER:"Nghe và trả lời (Speaking)",LISTEN_AND_SPEAK_IMAGE_GROUP:"Nghe, nhìn nhóm ảnh và trả lời (Speaking)",LISTEN_AND_SPEAK_QUESTION_LIST:"Nghe, chọn câu hỏi và trả lời (Speaking)",LISTEN_AND_SPEAK_COMPARE_IMAGES:"Nghe và so sánh ảnh (Speaking)",LISTEN_AND_SPEAK_WITH_STORY_IMAGES:"Nghe, nhìn ảnh truyện và kể lại (Speaking)",LISTEN_AND_SPEAK_ODD_ONE_OUT:"Nghe, chọn hình khác loại và giải thích (Speaking)",LISTEN_AND_SPEAK_INFO_EXCHANGE:"Trao đổi thông tin (Speaking)",READ_DISPLAYED_CONTENT:"Đọc nội dung hiển thị (Speaking)",SPEAKING_DESCRIBE_IMAGE:"Nhìn tranh và mô tả (Speaking Part 2)",SPONTANEOUS_QA:"Hỏi đáp tự phát (Speaking Part 3)",SPEAKING_CONVERSATION:"Hội thoại Speaking (KFS Speaking)",GN_SPEAKING_INTERVIEW:"GN Speaking - Hỏi đáp (Virtual + Real)"};export function getQuestionTypeLabel(E){return QUESTION_TYPE_LABELS[E]||E}export const SKILL_METADATA={[E.LISTENING]:{name:"Nghe",emoji:"🎧",color:"text-blue-600",bgColor:"bg-blue-50",borderColor:"border-blue-200",description:"Kỹ năng nghe hiểu"},[E.SPEAKING]:{name:"Nói",emoji:"🗣️",color:"text-green-600",bgColor:"bg-green-50",borderColor:"border-green-200",description:"Kỹ năng giao tiếp"},[E.READING]:{name:"Đọc",emoji:"📖",color:"text-purple-600",bgColor:"bg-purple-50",borderColor:"border-purple-200",description:"Kỹ năng đọc hiểu"},[E.WRITING]:{name:"Viết",emoji:"✏️",color:"text-orange-600",bgColor:"bg-orange-50",borderColor:"border-orange-200",description:"Kỹ năng viết"}};
@@ -29,7 +29,7 @@ export declare enum StudentSelectionType {
29
29
  }
30
30
  export type ResultStatus = 'PENDING' | 'PARTIAL_GRADED' | 'GRADED';
31
31
  export type StudentStatus = 'NOT_STARTED' | 'IN_PROGRESS' | 'SUBMITTED';
32
- export type QuestionType = 'FILL_IN_BLANK' | 'TRUE_FALSE' | 'MATCHING' | 'ORDERING' | 'LISTENING' | 'COLORING' | 'ESSAY' | 'LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE' | 'LISTEN_AND_TICK_ANSWER' | 'LISTEN_AND_FILL_NAME_NUMBER' | 'LISTENING_FILL_BLANK' | 'LISTENING_COLOR' | 'TICK_TRUE_OR_FALSE' | 'TICK_YES_OR_NO' | 'READING_TICK_CROSS' | 'ARRANGE_LETTERS_INTO_WORDS' | 'READ_PASSAGE_AND_COMPLETE_STORY' | 'CLOZE_TEST_WITH_TICK_BOX' | 'READ_AND_CHOOSE_APPROPRIATE_WORD' | 'WRITE_SHORT_PARAGRAPH' | 'LABEL_THE_PICTURE' | 'READING_LETTER_ARRANGE' | 'READING_PASSAGE_FILL' | 'FILL_BLANK' | 'FILL_MISSING_WORDS_IN_GRID' | 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER' | 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER' | 'LOOK_PICTURE_FILL_WORD_HINT' | 'LABEL_THE_PICTURE' | 'READ_AND_COLOR_OBJECTS' | 'IMAGE_OBJECT_MATCHING' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'TRUE_FALSE_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'WRITE_A_SHORT_LETTER' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_CORRECT_ADJECTIVE' | 'ANSWER_THE_QUESTION' | 'WORD_ORDERING' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'WRITE_SENTENCES' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPEAKING_CONVERSATION' | 'GN_SPEAKING_INTERVIEW' | 'SPEAKING_CUE_CARD' | 'WORD_FILL_STRUCTURED_FORM';
32
+ export type QuestionType = 'FILL_IN_BLANK' | 'TRUE_FALSE' | 'MATCHING' | 'ORDERING' | 'LISTENING' | 'COLORING' | 'ESSAY' | 'LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE' | 'LISTEN_AND_TICK_ANSWER' | 'LISTEN_AND_FILL_NAME_NUMBER' | 'LISTENING_FILL_BLANK' | 'LISTENING_COLOR' | 'TICK_TRUE_OR_FALSE' | 'TICK_YES_OR_NO' | 'READING_TICK_CROSS' | 'ARRANGE_LETTERS_INTO_WORDS' | 'READ_PASSAGE_AND_COMPLETE_STORY' | 'CLOZE_TEST_WITH_TICK_BOX' | 'READ_AND_CHOOSE_APPROPRIATE_WORD' | 'WRITE_SHORT_PARAGRAPH' | 'LABEL_THE_PICTURE' | 'READING_LETTER_ARRANGE' | 'READING_PASSAGE_FILL' | 'FILL_BLANK' | 'FILL_MISSING_WORDS_IN_GRID' | 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER' | 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER' | 'LOOK_PICTURE_FILL_WORD_HINT' | 'LABEL_THE_PICTURE' | 'READ_AND_COLOR_OBJECTS' | 'IMAGE_OBJECT_MATCHING' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'TRUE_FALSE_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'SPONTANEOUS_QA_GROUP' | 'WORD_ORDER_AND_MATCH_GROUP' | 'WRITE_A_SHORT_LETTER' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_CORRECT_ADJECTIVE' | 'ANSWER_THE_QUESTION' | 'WORD_ORDERING' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'WRITE_SENTENCES' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPEAKING_CONVERSATION' | 'GN_SPEAKING_INTERVIEW' | 'SPEAKING_CUE_CARD' | 'WORD_FILL_STRUCTURED_FORM';
33
33
  export type Level = 'Pre-A1' | 'A1' | 'A2' | 'B1' | 'B2' | 'C1' | 'C2';
34
34
  export type Difficulty = 'EASY' | 'MEDIUM' | 'HARD';
35
35
  export type Skill = 'LISTENING' | 'READING' | 'WRITING' | 'SPEAKING';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinyweb_dev/oe-exam-sdk",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "Reusable OceanEdu question components.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,8 @@
20
20
  "clean": "rm -rf dist",
21
21
  "build:compile": "tsc -p tsconfig.build.json",
22
22
  "build:alias": "tsc-alias -p tsconfig.build.json",
23
- "build:ts": "npm run build:compile && npm run build:assets && npm run build:alias",
23
+ "build:check-aliases": "node ./scripts/check-aliases.mjs",
24
+ "build:ts": "npm run build:compile && npm run build:assets && npm run build:alias && npm run build:check-aliases",
24
25
  "build:assets": "for d in cambridge-yle english-certification summer-sky; do [ -d src/components/themes/$d/assets ] && cp -R src/components/themes/$d/assets dist/components/themes/$d/ || true; done; [ -d src/components/shared/audio/assets ] && cp -R src/components/shared/audio/assets dist/components/shared/audio/ || true; [ -d src/components/results/report/assets ] && mkdir -p dist/components/results/report && cp -R src/components/results/report/assets dist/components/results/report/ || true; (cd src && find . -name '*.module.css' -exec cp --parents {} ../dist/ \\;); cp src/global.d.ts dist/global.d.ts",
25
26
  "minify:js": "find dist -name '*.js' -type f -exec sh -c 'for f do terser \"$f\" --compress directives=false --mangle --module --comments false --output \"$f\"; done' sh {} +",
26
27
  "build": "npm run clean && npm run build:ts && npm run minify:js",