@thanh01.pmt/interactive-quiz-kit 1.0.75 → 1.0.76

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.
package/dist/react-ui.mjs CHANGED
@@ -102301,7 +102301,7 @@ var contextOptions = [
102301
102301
  ];
102302
102302
 
102303
102303
  // src/react-ui/components/authoring/AIFullQuizGeneratorModal.tsx
102304
- var availableQuestionTypesForFullQuiz = [
102304
+ var ALL_AVAILABLE_QUESTION_TYPES = [
102305
102305
  { value: "true_false", label: "True/False" },
102306
102306
  { value: "multiple_choice", label: "Multiple Choice" },
102307
102307
  { value: "multiple_response", label: "Multiple Response" },
@@ -102316,8 +102316,11 @@ var AIFullQuizGeneratorModal = ({
102316
102316
  isOpen,
102317
102317
  onClose,
102318
102318
  onQuizGenerated,
102319
- language: language3
102320
- // <-- NHẬN PROP
102319
+ language: language3,
102320
+ maxQuestions = 100,
102321
+ // Default value
102322
+ allowedQuestionTypes = ALL_AVAILABLE_QUESTION_TYPES.map((qt) => qt.value)
102323
+ // Default value
102321
102324
  }) => {
102322
102325
  const [totalQuestions, setTotalQuestions] = useState(10);
102323
102326
  const [topics, setTopics] = useState([{ id: generateUniqueId("topic_"), topic: "", ratio: 100 }]);
@@ -102328,9 +102331,7 @@ var AIFullQuizGeneratorModal = ({
102328
102331
  ]);
102329
102332
  const [selectedContextIds, setSelectedContextIds] = useState([]);
102330
102333
  const [customContextInput, setCustomContextInput] = useState("");
102331
- const [selectedQuestionTypes, setSelectedQuestionTypes] = useState(
102332
- availableQuestionTypesForFullQuiz.map((qt) => qt.value)
102333
- );
102334
+ const [selectedQuestionTypes, setSelectedQuestionTypes] = useState([]);
102334
102335
  const [authorizeAISkipReview, setAuthorizeAISkipReview] = useState(false);
102335
102336
  const [isLoading, setIsLoading] = useState(false);
102336
102337
  const [error, setError] = useState(null);
@@ -102339,6 +102340,9 @@ var AIFullQuizGeneratorModal = ({
102339
102340
  const { toast: toast2 } = useToast();
102340
102341
  const [isApiKeyManagerModalOpen, setIsApiKeyManagerModalOpen] = useState(false);
102341
102342
  const [geminiApiKeyExists, setGeminiApiKeyExists] = useState(false);
102343
+ const availableQuestionTypesForFullQuiz = useMemo(() => {
102344
+ return ALL_AVAILABLE_QUESTION_TYPES.filter((qt) => allowedQuestionTypes.includes(qt.value));
102345
+ }, [allowedQuestionTypes]);
102342
102346
  useEffect(() => {
102343
102347
  if (isOpen) {
102344
102348
  setTotalQuestions(10);
@@ -102358,7 +102362,7 @@ var AIFullQuizGeneratorModal = ({
102358
102362
  setAiGeneratedPlan(null);
102359
102363
  setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
102360
102364
  }
102361
- }, [isOpen]);
102365
+ }, [isOpen, availableQuestionTypesForFullQuiz]);
102362
102366
  const handleApiKeyModalClose = () => {
102363
102367
  setIsApiKeyManagerModalOpen(false);
102364
102368
  setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
@@ -102366,7 +102370,7 @@ var AIFullQuizGeneratorModal = ({
102366
102370
  const handleTotalQuestionsChange = (e3) => {
102367
102371
  let num = parseInt(e3.target.value, 10);
102368
102372
  if (isNaN(num) || num < 1) num = 1;
102369
- if (num > 100) num = 100;
102373
+ if (num > maxQuestions) num = maxQuestions;
102370
102374
  setTotalQuestions(num);
102371
102375
  };
102372
102376
  const handleTopicChange = (id3, field, value) => {
@@ -102396,8 +102400,8 @@ var AIFullQuizGeneratorModal = ({
102396
102400
  setError("Gemini API Key is required for AI features. Please set it first.");
102397
102401
  return false;
102398
102402
  }
102399
- if (totalQuestions <= 0 || totalQuestions > 100) {
102400
- setError("Total questions must be between 1 and 100.");
102403
+ if (totalQuestions <= 0 || totalQuestions > maxQuestions) {
102404
+ setError(`Total questions must be between 1 and ${maxQuestions}.`);
102401
102405
  return false;
102402
102406
  }
102403
102407
  if (topics.some((t4) => !t4.topic.trim())) {
@@ -102436,7 +102440,6 @@ var AIFullQuizGeneratorModal = ({
102436
102440
  const stage2Input = {
102437
102441
  quizPlan: plan,
102438
102442
  language: language3,
102439
- // <-- SỬ DỤNG PROP
102440
102443
  imageContexts: []
102441
102444
  };
102442
102445
  const stage2Output = await generateQuestionsFromQuizPlan(stage2Input, apiKey);
@@ -102487,7 +102490,6 @@ var AIFullQuizGeneratorModal = ({
102487
102490
  try {
102488
102491
  const planInput = {
102489
102492
  language: language3,
102490
- // <-- SỬ DỤNG PROP
102491
102493
  totalQuestions,
102492
102494
  numCodingQuestions: 0,
102493
102495
  topics: topics.map((t4) => ({ topic: t4.topic, ratio: t4.ratio })),
@@ -102551,103 +102553,9 @@ var AIFullQuizGeneratorModal = ({
102551
102553
  };
102552
102554
  const renderContent3 = () => {
102553
102555
  if (currentStage === "review") {
102554
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 py-4" }, /* @__PURE__ */ React169__default.createElement("h3", { className: "text-lg font-Medium mb-2 text-primary flex items-center" }, /* @__PURE__ */ React169__default.createElement(Eye, { className: "mr-2 h-5 w-5" }), " Review & Adjust AI Generated Quiz Plan"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, "The AI has proposed the following plan. You can change question types, reorder, or remove questions before final generation. Note: 'Drag and Drop' type questions in the plan will be created as placeholders and require manual authoring of items/zones/answers."), aiGeneratedPlan && aiGeneratedPlan.length > 0 ? /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "max-h-[calc(60vh - 120px)] pr-3" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, aiGeneratedPlan.map((plannedQ, index3) => /* @__PURE__ */ React169__default.createElement(Card, { key: `planned-${index3}-${plannedQ.plannedTopic.replace(/\s/g, "")}`, className: "p-3" }, /* @__PURE__ */ React169__default.createElement(CardContent, { className: "p-0 space-y-2" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold text-sm" }, "Q", index3 + 1, ": ", plannedQ.plannedTopic), /* @__PURE__ */ React169__default.createElement("div", { className: "flex space-x-1" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "up"), disabled: index3 === 0 }, /* @__PURE__ */ React169__default.createElement(ArrowUp, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "down"), disabled: index3 === aiGeneratedPlan.length - 1 }, /* @__PURE__ */ React169__default.createElement(ArrowDown, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7 text-destructive", onClick: () => handleRemovePlannedQuestion(index3) }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-3 items-end" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `review-qtype-${index3}`, className: "text-xs" }, "Question Type"), /* @__PURE__ */ React169__default.createElement(
102555
- Select2,
102556
- {
102557
- value: plannedQ.plannedQuestionType,
102558
- onValueChange: (value) => handleChangePlannedQuestionType(index3, value)
102559
- },
102560
- /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: `review-qtype-${index3}`, className: "h-9 text-xs" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)),
102561
- /* @__PURE__ */ React169__default.createElement(SelectContent2, null, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: qType.value, value: qType.value, className: "text-xs" }, qType.label)))
102562
- )), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "text-xs block" }, "Bloom Level"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs p-2 h-9 border rounded-md bg-secondary/50 flex items-center capitalize" }, plannedQ.plannedBloomLevel)))))))) : /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, "No quiz plan generated or plan is empty."), error && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error));
102556
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 py-4" }, /* @__PURE__ */ React169__default.createElement("h3", { className: "text-lg font-Medium mb-2 text-primary flex items-center" }, /* @__PURE__ */ React169__default.createElement(Eye, { className: "mr-2 h-5 w-5" }), " Review & Adjust AI Generated Quiz Plan"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, "The AI has proposed the following plan. You can change question types, reorder, or remove questions before final generation. Note: 'Drag and Drop' type questions in the plan will be created as placeholders and require manual authoring of items/zones/answers."), aiGeneratedPlan && aiGeneratedPlan.length > 0 ? /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "max-h-[calc(60vh - 120px)] pr-3" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, aiGeneratedPlan.map((plannedQ, index3) => /* @__PURE__ */ React169__default.createElement(Card, { key: `planned-${index3}-${plannedQ.plannedTopic.replace(/\s/g, "")}`, className: "p-3" }, /* @__PURE__ */ React169__default.createElement(CardContent, { className: "p-0 space-y-2" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold text-sm" }, "Q", index3 + 1, ": ", plannedQ.plannedTopic), /* @__PURE__ */ React169__default.createElement("div", { className: "flex space-x-1" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "up"), disabled: index3 === 0 }, /* @__PURE__ */ React169__default.createElement(ArrowUp, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "down"), disabled: index3 === aiGeneratedPlan.length - 1 }, /* @__PURE__ */ React169__default.createElement(ArrowDown, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7 text-destructive", onClick: () => handleRemovePlannedQuestion(index3) }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-3 items-end" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `review-qtype-${index3}`, className: "text-xs" }, "Question Type"), /* @__PURE__ */ React169__default.createElement(Select2, { value: plannedQ.plannedQuestionType, onValueChange: (value) => handleChangePlannedQuestionType(index3, value) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: `review-qtype-${index3}`, className: "h-9 text-xs" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: qType.value, value: qType.value, className: "text-xs" }, qType.label))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "text-xs block" }, "Bloom Level"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs p-2 h-9 border rounded-md bg-secondary/50 flex items-center capitalize" }, plannedQ.plannedBloomLevel)))))))) : /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, "No quiz plan generated or plan is empty."), error && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error));
102563
102557
  }
102564
- return /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "max-h-[calc(70vh - 100px)] pl-4 pr-3 py-1" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 py-1 px-2" }, !geminiApiKeyExists && /* @__PURE__ */ React169__default.createElement("div", { className: "p-3 mb-4 border border-amber-500 bg-amber-50 rounded-md text-amber-700" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-start" }, /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "h-5 w-5 mr-2 mt-0.5 flex-shrink-0" }), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold" }, "Gemini API Key Required"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs" }, "To use AI quiz generation, please set your Google Gemini API Key."), /* @__PURE__ */ React169__default.createElement(
102565
- Button,
102566
- {
102567
- variant: "link",
102568
- className: "p-0 h-auto text-xs text-amber-700 hover:text-amber-800 mt-1",
102569
- onClick: () => setIsApiKeyManagerModalOpen(true)
102570
- },
102571
- /* @__PURE__ */ React169__default.createElement(Settings, { className: "mr-1 h-3 w-3" }),
102572
- " Set API Key Now"
102573
- )))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "total-questions" }, "Total Number of Questions (1-100)"), /* @__PURE__ */ React169__default.createElement(
102574
- Input,
102575
- {
102576
- id: "total-questions",
102577
- type: "number",
102578
- value: totalQuestions,
102579
- onChange: handleTotalQuestionsChange,
102580
- min: "1",
102581
- max: "100"
102582
- }
102583
- )), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Topic Distribution"), topics.map((topicItem, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: topicItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__default.createElement(
102584
- Input,
102585
- {
102586
- type: "text",
102587
- placeholder: `Topic ${index3 + 1}`,
102588
- value: topicItem.topic,
102589
- onChange: (e3) => handleTopicChange(topicItem.id, "topic", e3.target.value),
102590
- className: "flex-grow"
102591
- }
102592
- ), /* @__PURE__ */ React169__default.createElement(
102593
- Input,
102594
- {
102595
- type: "number",
102596
- placeholder: "Ratio %",
102597
- value: topicItem.ratio,
102598
- onChange: (e3) => handleTopicChange(topicItem.id, "ratio", parseInt(e3.target.value, 10) || 0),
102599
- className: "w-24",
102600
- min: "0",
102601
- max: "100"
102602
- }
102603
- ), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeTopic(topicItem.id), disabled: topics.length <= 1 }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: addTopic }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic")), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Bloom Level Distribution"), bloomLevels.map((bloomItem) => /* @__PURE__ */ React169__default.createElement("div", { key: bloomItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "w-32 capitalize flex-shrink-0" }, bloomItem.level), /* @__PURE__ */ React169__default.createElement(
102604
- Input,
102605
- {
102606
- type: "number",
102607
- placeholder: "Ratio %",
102608
- value: bloomItem.ratio,
102609
- onChange: (e3) => handleBloomRatioChange(bloomItem.id, parseInt(e3.target.value, 10) || 0),
102610
- className: "w-24",
102611
- min: "0",
102612
- max: "100"
102613
- }
102614
- )))), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Relevant Contexts (Optional, Select Multiple)"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, contextOptions.filter((opt) => opt.contextId !== "__none__" && opt.contextId !== "__custom__").map((opt) => /* @__PURE__ */ React169__default.createElement("div", { key: opt.contextId, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(
102615
- Checkbox2,
102616
- {
102617
- id: `ctx-full-${opt.contextId}`,
102618
- checked: selectedContextIds.includes(opt.contextId),
102619
- onCheckedChange: () => handleContextToggle(opt.contextId)
102620
- }
102621
- ), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `ctx-full-${opt.contextId}`, className: "text-xs font-normal cursor-pointer" }, opt.contextDescription, " (", opt.shortContextId, ")")))), /* @__PURE__ */ React169__default.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React169__default.createElement(
102622
- Checkbox2,
102623
- {
102624
- id: `ctx-full-__custom__`,
102625
- checked: selectedContextIds.includes("__custom__"),
102626
- onCheckedChange: () => handleContextToggle("__custom__")
102627
- }
102628
- ), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `ctx-full-__custom__`, className: "text-xs font-normal cursor-pointer ml-2" }, "Other (Custom Input)")), selectedContextIds.includes("__custom__") && /* @__PURE__ */ React169__default.createElement(
102629
- Textarea,
102630
- {
102631
- value: customContextInput,
102632
- onChange: (e3) => setCustomContextInput(e3.target.value),
102633
- placeholder: "Enter your specific custom context here...",
102634
- className: "min-h-[60px] mt-2 text-sm"
102635
- }
102636
- )), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Question Types to Use (Select Multiple)"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__default.createElement("div", { key: qType.value, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(
102637
- Checkbox2,
102638
- {
102639
- id: `qtype-full-${qType.value}`,
102640
- checked: selectedQuestionTypes.includes(qType.value),
102641
- onCheckedChange: () => handleQuestionTypeToggle(qType.value)
102642
- }
102643
- ), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `qtype-full-${qType.value}`, className: "text-xs font-normal cursor-pointer" }, qType.label))))), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-2 pt-2" }, /* @__PURE__ */ React169__default.createElement(
102644
- Checkbox2,
102645
- {
102646
- id: "authorize-ai-skip-review",
102647
- checked: authorizeAISkipReview,
102648
- onCheckedChange: (checked) => setAuthorizeAISkipReview(!!checked)
102649
- }
102650
- ), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "authorize-ai-skip-review", className: "text-sm font-normal" }, "Authorize AI (Skip Review & Generate Directly)")), error && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error)));
102558
+ return /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "max-h-[calc(70vh - 100px)] pl-4 pr-3 py-1" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 py-1 px-2" }, !geminiApiKeyExists && /* @__PURE__ */ React169__default.createElement("div", { className: "p-3 mb-4 border border-amber-500 bg-amber-50 rounded-md text-amber-700" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-start" }, /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "h-5 w-5 mr-2 mt-0.5 flex-shrink-0" }), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold" }, "Gemini API Key Required"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs" }, "To use AI quiz generation, please set your Google Gemini API Key."), /* @__PURE__ */ React169__default.createElement(Button, { variant: "link", className: "p-0 h-auto text-xs text-amber-700 hover:text-amber-800 mt-1", onClick: () => setIsApiKeyManagerModalOpen(true) }, /* @__PURE__ */ React169__default.createElement(Settings, { className: "mr-1 h-3 w-3" }), " Set API Key Now")))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "total-questions" }, "Total Number of Questions (1-", maxQuestions, ")"), /* @__PURE__ */ React169__default.createElement(Input, { id: "total-questions", type: "number", value: totalQuestions, onChange: handleTotalQuestionsChange, min: "1", max: maxQuestions })), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Topic Distribution"), topics.map((topicItem, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: topicItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__default.createElement(Input, { type: "text", placeholder: `Topic ${index3 + 1}`, value: topicItem.topic, onChange: (e3) => handleTopicChange(topicItem.id, "topic", e3.target.value), className: "flex-grow" }), /* @__PURE__ */ React169__default.createElement(Input, { type: "number", placeholder: "Ratio %", value: topicItem.ratio, onChange: (e3) => handleTopicChange(topicItem.id, "ratio", parseInt(e3.target.value, 10) || 0), className: "w-24", min: "0", max: "100" }), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeTopic(topicItem.id), disabled: topics.length <= 1 }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: addTopic }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic")), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Bloom Level Distribution"), bloomLevels.map((bloomItem) => /* @__PURE__ */ React169__default.createElement("div", { key: bloomItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "w-32 capitalize flex-shrink-0" }, bloomItem.level), /* @__PURE__ */ React169__default.createElement(Input, { type: "number", placeholder: "Ratio %", value: bloomItem.ratio, onChange: (e3) => handleBloomRatioChange(bloomItem.id, parseInt(e3.target.value, 10) || 0), className: "w-24", min: "0", max: "100" })))), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Relevant Contexts (Optional, Select Multiple)"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, contextOptions.filter((opt) => opt.contextId !== "__none__" && opt.contextId !== "__custom__").map((opt) => /* @__PURE__ */ React169__default.createElement("div", { key: opt.contextId, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(Checkbox2, { id: `ctx-full-${opt.contextId}`, checked: selectedContextIds.includes(opt.contextId), onCheckedChange: () => handleContextToggle(opt.contextId) }), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `ctx-full-${opt.contextId}`, className: "text-xs font-normal cursor-pointer" }, opt.contextDescription, " (", opt.shortContextId, ")"))), /* @__PURE__ */ React169__default.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React169__default.createElement(Checkbox2, { id: `ctx-full-__custom__`, checked: selectedContextIds.includes("__custom__"), onCheckedChange: () => handleContextToggle("__custom__") }), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `ctx-full-__custom__`, className: "text-xs font-normal cursor-pointer ml-2" }, "Other (Custom Input)")), selectedContextIds.includes("__custom__") && /* @__PURE__ */ React169__default.createElement(Textarea, { value: customContextInput, onChange: (e3) => setCustomContextInput(e3.target.value), placeholder: "Enter your specific custom context here...", className: "min-h-[60px] mt-2 text-sm" }))), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Question Types to Use (Select Multiple)"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__default.createElement("div", { key: qType.value, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(Checkbox2, { id: `qtype-full-${qType.value}`, checked: selectedQuestionTypes.includes(qType.value), onCheckedChange: () => handleQuestionTypeToggle(qType.value) }), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `qtype-full-${qType.value}`, className: "text-xs font-normal cursor-pointer" }, qType.label))))), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-2 pt-2" }, /* @__PURE__ */ React169__default.createElement(Checkbox2, { id: "authorize-ai-skip-review", checked: authorizeAISkipReview, onCheckedChange: (checked) => setAuthorizeAISkipReview(!!checked) }), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "authorize-ai-skip-review", className: "text-sm font-normal" }, "Authorize AI (Skip Review & Generate Directly)")), error && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error)));
102651
102559
  };
102652
102560
  return /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => !open && onClose() }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-[650px] md:max-w-[750px] lg:max-w-[800px] max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, { className: "flex items-center font-headline text-2xl" }, /* @__PURE__ */ React169__default.createElement(FileText, { className: "mr-2 h-6 w-6 text-primary" }), " Generate Full Quiz with AI"), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, currentStage === "input" && "Define parameters for AI to generate a quiz plan.", currentStage === "review" && "Review and adjust the AI-generated quiz plan before generating questions.", currentStage === "generating" && "AI is working... Please wait.")), renderContent3(), /* @__PURE__ */ React169__default.createElement(DialogFooter, { className: "pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: onClose }, "Cancel")), currentStage === "input" && /* @__PURE__ */ React169__default.createElement(Button, { type: "button", onClick: handleSubmitPlanGeneration, disabled: isLoading || !geminiApiKeyExists }, isLoading ? /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), authorizeAISkipReview ? "Generate Quiz Directly" : "Generate Quiz Plan"), currentStage === "review" && !isLoading && /* @__PURE__ */ React169__default.createElement(Button, { type: "button", onClick: handleGenerateQuestionsFromReviewedPlan, disabled: isLoading || !aiGeneratedPlan || aiGeneratedPlan.length === 0 || !geminiApiKeyExists }, isLoading ? /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), "Generate Questions from Plan"), currentStage === "generating" && isLoading && /* @__PURE__ */ React169__default.createElement(Button, { type: "button", disabled: true }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Generating... Please Wait")))), /* @__PURE__ */ React169__default.createElement(APIKeyManagerModal, { isOpen: isApiKeyManagerModalOpen, onClose: handleApiKeyModalClose }));
102653
102561
  };
@@ -138253,58 +138161,74 @@ var AVAILABLE_LANGUAGES = [
138253
138161
  { value: "Vietnamese", label: "Ti\u1EBFng Vi\u1EC7t" },
138254
138162
  { value: "English", label: "English" }
138255
138163
  ];
138164
+ var ALL_QUESTION_TYPE_LABELS = {
138165
+ multiple_choice: "Multiple Choice",
138166
+ multiple_response: "Multiple Response",
138167
+ true_false: "True/False",
138168
+ short_answer: "Short Answer",
138169
+ numeric: "Numeric",
138170
+ fill_in_the_blanks: "Fill in the Blanks",
138171
+ sequence: "Sequence",
138172
+ matching: "Matching",
138173
+ drag_and_drop: "Drag & Drop",
138174
+ hotspot: "Hotspot",
138175
+ blockly_programming: "Blockly",
138176
+ scratch_programming: "Scratch",
138177
+ coding: "Coding"
138178
+ };
138256
138179
  var PracticeSetup = ({
138257
138180
  onStartPractice,
138258
138181
  isGenerating,
138259
138182
  initialLOs,
138260
138183
  initialDifficulty,
138261
- initialLanguage
138184
+ initialLanguage,
138185
+ maxQuestions = 20,
138186
+ // Default max questions
138187
+ allowedQuestionTypes = ["true_false", "multiple_choice", "multiple_response"]
138188
+ // Default allowed types
138262
138189
  }) => {
138263
138190
  const { t: t4 } = useTranslation();
138264
138191
  const [subjects, setSubjects] = useState([]);
138265
- const [categories, setCategories] = useState([]);
138266
- const [topics, setTopics] = useState([]);
138192
+ const [categoriesBySubject, setCategoriesBySubject] = useState({});
138193
+ const [topicsByCategory, setTopicsByCategory] = useState({});
138267
138194
  const [hasData, setHasData] = useState(false);
138268
138195
  const [selectedSubject, setSelectedSubject] = useState("");
138269
- const [selectedCategory, setSelectedCategory] = useState("");
138270
138196
  const [selectedTopics, setSelectedTopics] = useState(/* @__PURE__ */ new Set());
138271
138197
  const [selectedLanguage, setSelectedLanguage] = useState(AVAILABLE_LANGUAGES[0].value);
138272
138198
  const [selectedDifficulty, setSelectedDifficulty] = useState("");
138273
- const [includeCodingQuestions, setIncludeCodingQuestions] = useState(false);
138274
- const [numCodingQuestions, setNumCodingQuestions] = useState(1);
138199
+ const [numQuestions, setNumQuestions] = useState(10);
138200
+ const [selectedQuestionTypes, setSelectedQuestionTypes] = useState(new Set(allowedQuestionTypes));
138275
138201
  useEffect(() => {
138276
- const allSubjects = TopicDataService.getSubjects();
138277
- setHasData(allSubjects.length > 0);
138278
- setSubjects(allSubjects);
138202
+ const allLOs = TopicDataService.getData();
138203
+ if (allLOs.length > 0) {
138204
+ setHasData(true);
138205
+ const uniqueSubjects = [...new Set(allLOs.map((lo) => lo.subject))].sort();
138206
+ setSubjects(uniqueSubjects);
138207
+ const catsBySub = {};
138208
+ uniqueSubjects.forEach((sub) => {
138209
+ catsBySub[sub] = [...new Set(allLOs.filter((lo) => lo.subject === sub).map((lo) => lo.category))].sort();
138210
+ });
138211
+ setCategoriesBySubject(catsBySub);
138212
+ const topsByCat = {};
138213
+ allLOs.forEach((lo) => {
138214
+ if (!topsByCat[lo.category]) {
138215
+ topsByCat[lo.category] = [];
138216
+ }
138217
+ topsByCat[lo.category].push(lo.topic);
138218
+ });
138219
+ Object.keys(topsByCat).forEach((cat) => {
138220
+ topsByCat[cat] = [...new Set(topsByCat[cat])].sort();
138221
+ });
138222
+ setTopicsByCategory(topsByCat);
138223
+ }
138279
138224
  if (initialLOs && initialLOs.length > 0) {
138280
138225
  const firstLO = initialLOs[0];
138281
138226
  setSelectedSubject(firstLO.subject);
138282
- setSelectedCategory(firstLO.category);
138283
138227
  setSelectedTopics(new Set(initialLOs.map((lo) => lo.topic)));
138284
138228
  setSelectedDifficulty(initialDifficulty || "Medium");
138285
138229
  setSelectedLanguage(initialLanguage || AVAILABLE_LANGUAGES[0].value);
138286
138230
  }
138287
138231
  }, [initialLOs, initialDifficulty, initialLanguage]);
138288
- useEffect(() => {
138289
- if (selectedSubject) {
138290
- const filteredCategories = TopicDataService.getCategoriesBySubject(selectedSubject);
138291
- setCategories(filteredCategories);
138292
- if (!initialLOs) {
138293
- setSelectedCategory("");
138294
- setTopics([]);
138295
- setSelectedTopics(/* @__PURE__ */ new Set());
138296
- }
138297
- }
138298
- }, [selectedSubject, initialLOs]);
138299
- useEffect(() => {
138300
- if (selectedCategory) {
138301
- const filteredTopics = TopicDataService.getTopicsByCategory(selectedCategory);
138302
- setTopics(filteredTopics);
138303
- if (!initialLOs) {
138304
- setSelectedTopics(/* @__PURE__ */ new Set());
138305
- }
138306
- }
138307
- }, [selectedCategory, initialLOs]);
138308
138232
  const handleTopicToggle = useCallback((topic, checked) => {
138309
138233
  setSelectedTopics((prevSelected) => {
138310
138234
  const newSelected = new Set(prevSelected);
@@ -138313,37 +138237,36 @@ var PracticeSetup = ({
138313
138237
  return newSelected;
138314
138238
  });
138315
138239
  }, []);
138240
+ const handleQuestionTypeToggle = useCallback((type, checked) => {
138241
+ setSelectedQuestionTypes((prev) => {
138242
+ const newSet = new Set(prev);
138243
+ if (checked) newSet.add(type);
138244
+ else newSet.delete(type);
138245
+ return newSet;
138246
+ });
138247
+ }, []);
138316
138248
  const handleStartClick = useCallback(() => {
138317
- if (selectedTopics.size > 0 && selectedDifficulty && selectedLanguage) {
138249
+ if (selectedTopics.size > 0 && selectedDifficulty && selectedLanguage && numQuestions > 0 && selectedQuestionTypes.size > 0) {
138318
138250
  const selectedLOs = TopicDataService.getLearningObjectivesByTopics(Array.from(selectedTopics));
138319
- onStartPractice(selectedLOs, selectedDifficulty, selectedLanguage, includeCodingQuestions, numCodingQuestions);
138251
+ onStartPractice(selectedLOs, selectedDifficulty, selectedLanguage, numQuestions, Array.from(selectedQuestionTypes));
138320
138252
  }
138321
- }, [onStartPractice, selectedDifficulty, selectedLanguage, selectedTopics, includeCodingQuestions, numCodingQuestions]);
138322
- const handleNumCodingChange = (e3) => {
138253
+ }, [onStartPractice, selectedDifficulty, selectedLanguage, selectedTopics, numQuestions, selectedQuestionTypes]);
138254
+ const handleNumQuestionsChange = (e3) => {
138323
138255
  let value = parseInt(e3.target.value, 10);
138324
138256
  if (isNaN(value) || value < 1) value = 1;
138325
- if (value > 3) value = 3;
138326
- setNumCodingQuestions(value);
138257
+ if (value > maxQuestions) value = maxQuestions;
138258
+ setNumQuestions(value);
138327
138259
  };
138328
138260
  const isReadyToStart = useMemo(() => {
138329
- return selectedTopics.size > 0 && !!selectedDifficulty && !!selectedLanguage && !isGenerating;
138330
- }, [selectedTopics, selectedDifficulty, selectedLanguage, isGenerating]);
138261
+ return selectedTopics.size > 0 && !!selectedDifficulty && !!selectedLanguage && numQuestions > 0 && selectedQuestionTypes.size > 0 && !isGenerating;
138262
+ }, [selectedTopics, selectedDifficulty, selectedLanguage, numQuestions, selectedQuestionTypes, isGenerating]);
138331
138263
  if (!hasData) {
138332
138264
  return /* @__PURE__ */ React169__default.createElement(Card, { className: "w-full max-w-2xl mx-auto" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex items-center text-2xl font-headline" }, /* @__PURE__ */ React169__default.createElement(BrainCircuit, { className: "mr-3 h-6 w-6 text-primary" }), t4("practiceFlow.setup.title"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React169__default.createElement(CircleAlert, { className: "h-4 w-4" }), /* @__PURE__ */ React169__default.createElement(AlertTitle, null, "No Learning Objective Data Found"), /* @__PURE__ */ React169__default.createElement(AlertDescription, null, 'Please import your curriculum data from a TSV file in the "Manage Topics" section before you can start a practice session.'))));
138333
138265
  }
138334
- return /* @__PURE__ */ React169__default.createElement(Card, { className: "w-full max-w-2xl mx-auto" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex items-center text-2xl font-headline" }, /* @__PURE__ */ React169__default.createElement(BrainCircuit, { className: "mr-3 h-6 w-6 text-primary" }), t4("practiceFlow.setup.title")), /* @__PURE__ */ React169__default.createElement(CardDescription, null, t4("practiceFlow.setup.description"))), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subject-select", className: "font-semibold" }, t4("practiceFlow.setup.step1_title")), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedSubject, onValueChange: setSelectedSubject, disabled: isGenerating }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "subject-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("practiceFlow.setup.step1_placeholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, subjects.map((subject) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: subject, value: subject }, subject))))), selectedSubject && /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2 animate-in fade-in duration-300" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "category-select", className: "font-semibold" }, t4("practiceFlow.setup.step2_title")), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedCategory, onValueChange: setSelectedCategory, disabled: !selectedSubject || isGenerating }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "category-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("practiceFlow.setup.step2_placeholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, categories.map((category) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: category, value: category }, category))))), selectedCategory && /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2 animate-in fade-in duration-500" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, t4("practiceFlow.setup.step3_title")), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-48 w-full rounded-md border p-4" }, topics.length > 0 ? /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, topics.map((topic) => /* @__PURE__ */ React169__default.createElement("div", { key: topic, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(Checkbox2, { id: `topic-${topic.replace(/\s/g, "-")}`, checked: selectedTopics.has(topic), onCheckedChange: (checked) => handleTopicToggle(topic, !!checked), disabled: isGenerating }), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `topic-${topic.replace(/\s/g, "-")}`, className: "font-normal cursor-pointer" }, topic)))) : /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("practiceFlow.setup.step3_empty")))), selectedTopics.size > 0 && /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-lg animate-in fade-in duration-700" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "language-select", className: "font-semibold" }, t4("practiceFlow.setup.step4_title")), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedLanguage, onValueChange: setSelectedLanguage, disabled: isGenerating }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "language-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("practiceFlow.setup.step4_placeholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, AVAILABLE_LANGUAGES.map((lang) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: lang.value, value: lang.value }, lang.label))))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "difficulty-select", className: "font-semibold" }, t4("practiceFlow.setup.step5_title")), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedDifficulty, onValueChange: (v) => setSelectedDifficulty(v), disabled: isGenerating }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "difficulty-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("practiceFlow.setup.step5_placeholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, DIFFICULTY_LEVELS.map((level) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: level, value: level }, level)))))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold flex items-center" }, /* @__PURE__ */ React169__default.createElement(Code, { className: "mr-2 h-4 w-4" }), t4("practiceFlow.setup.step6_title")), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(Checkbox2, { id: "include-coding", checked: includeCodingQuestions, onCheckedChange: (checked) => setIncludeCodingQuestions(!!checked), disabled: isGenerating }), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "include-coding", className: "font-normal cursor-pointer" }, t4("practiceFlow.setup.includeCoding"))), includeCodingQuestions && /* @__PURE__ */ React169__default.createElement("div", { className: "pl-6 animate-in fade-in duration-300" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "num-coding-questions" }, t4("practiceFlow.setup.numCoding")), /* @__PURE__ */ React169__default.createElement(
138335
- Input,
138336
- {
138337
- id: "num-coding-questions",
138338
- type: "number",
138339
- value: numCodingQuestions,
138340
- onChange: handleNumCodingChange,
138341
- min: "1",
138342
- max: "3",
138343
- className: "w-24 mt-1",
138344
- disabled: isGenerating
138345
- }
138346
- ))))), /* @__PURE__ */ React169__default.createElement(CardFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleStartClick, disabled: !isReadyToStart, className: "w-full" }, isGenerating ? /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t4("practiceFlow.setup.generatingButton")) : /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), t4("practiceFlow.setup.startButton")))));
138266
+ return /* @__PURE__ */ React169__default.createElement(Card, { className: "w-full max-w-2xl mx-auto" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex items-center text-2xl font-headline" }, /* @__PURE__ */ React169__default.createElement(BrainCircuit, { className: "mr-3 h-6 w-6 text-primary" }), t4("practiceFlow.setup.title")), /* @__PURE__ */ React169__default.createElement(CardDescription, null, t4("practiceFlow.setup.description"))), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subject-select", className: "font-semibold" }, t4("practiceFlow.setup.step1_title")), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedSubject, onValueChange: (val) => {
138267
+ setSelectedSubject(val);
138268
+ setSelectedTopics(/* @__PURE__ */ new Set());
138269
+ }, disabled: isGenerating }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "subject-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("practiceFlow.setup.step1_placeholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, subjects.map((subject) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: subject, value: subject }, subject))))), selectedSubject && /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2 animate-in fade-in duration-300" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, t4("practiceFlow.setup.step2_title_combined")), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-48 w-full rounded-md border p-2" }, /* @__PURE__ */ React169__default.createElement(Accordion2, { type: "multiple", className: "w-full" }, (categoriesBySubject[selectedSubject] || []).map((category) => /* @__PURE__ */ React169__default.createElement(AccordionItem2, { value: category, key: category }, /* @__PURE__ */ React169__default.createElement(AccordionTrigger2, { className: "text-sm font-medium px-2" }, category), /* @__PURE__ */ React169__default.createElement(AccordionContent2, { className: "pl-4" }, (topicsByCategory[category] || []).map((topic) => /* @__PURE__ */ React169__default.createElement("div", { key: topic, className: "flex items-center space-x-2 py-1" }, /* @__PURE__ */ React169__default.createElement(Checkbox2, { id: `topic-${topic.replace(/\s/g, "-")}`, checked: selectedTopics.has(topic), onCheckedChange: (checked) => handleTopicToggle(topic, !!checked), disabled: isGenerating }), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `topic-${topic.replace(/\s/g, "-")}`, className: "font-normal cursor-pointer" }, topic))))))))), selectedTopics.size > 0 && /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-lg animate-in fade-in duration-700" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "num-questions", className: "font-semibold" }, t4("practiceFlow.setup.step_num_questions", { max: maxQuestions })), /* @__PURE__ */ React169__default.createElement(Input, { id: "num-questions", type: "number", value: numQuestions, onChange: handleNumQuestionsChange, min: "1", max: maxQuestions, disabled: isGenerating })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "difficulty-select", className: "font-semibold" }, t4("practiceFlow.setup.step5_title")), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedDifficulty, onValueChange: (v) => setSelectedDifficulty(v), disabled: isGenerating }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "difficulty-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("practiceFlow.setup.step5_placeholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, DIFFICULTY_LEVELS.map((level) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: level, value: level }, level)))))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, t4("practiceFlow.setup.step_question_types")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-2" }, allowedQuestionTypes.map((type) => /* @__PURE__ */ React169__default.createElement("div", { key: type, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(Checkbox2, { id: `qtype-${type}`, checked: selectedQuestionTypes.has(type), onCheckedChange: (checked) => handleQuestionTypeToggle(type, !!checked), disabled: isGenerating }), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `qtype-${type}`, className: "font-normal cursor-pointer text-sm" }, ALL_QUESTION_TYPE_LABELS[type] || type))))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "language-select", className: "font-semibold" }, t4("practiceFlow.setup.step4_title")), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedLanguage, onValueChange: setSelectedLanguage, disabled: isGenerating }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "language-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("practiceFlow.setup.step4_placeholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, AVAILABLE_LANGUAGES.map((lang) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: lang.value, value: lang.value }, lang.label))))))), /* @__PURE__ */ React169__default.createElement(CardFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleStartClick, disabled: !isReadyToStart, className: "w-full" }, isGenerating ? /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t4("practiceFlow.setup.generatingButton")) : /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), t4("practiceFlow.setup.startButton")))));
138347
138270
  };
138348
138271
 
138349
138272
  // src/react-ui/components/practice/PracticeModeController.tsx
@@ -138363,7 +138286,10 @@ var mapDifficultyToBloomDistribution = (difficulty) => {
138363
138286
  return [{ level: "remembering", ratio: 50 }, { level: "understanding", ratio: 50 }];
138364
138287
  }
138365
138288
  };
138366
- var PracticeModeController = () => {
138289
+ var PracticeModeController = ({
138290
+ maxQuestions,
138291
+ allowedQuestionTypes
138292
+ }) => {
138367
138293
  const router = useRouter();
138368
138294
  const [practiceState, setPracticeState] = useState("initializing");
138369
138295
  const [quizConfig, setQuizConfig] = useState(null);
@@ -138424,7 +138350,7 @@ var PracticeModeController = () => {
138424
138350
  setPracticeState("setup");
138425
138351
  }
138426
138352
  }, [toast2]);
138427
- const handleStartPractice = useCallback(async (selectedLOs, difficulty, language3, includeCoding, numCoding) => {
138353
+ const handleStartPractice = useCallback(async (selectedLOs, difficulty, language3, numQuestions, selectedQuestionTypes) => {
138428
138354
  setPracticeState("loading");
138429
138355
  setErrorMessage("");
138430
138356
  const apiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
@@ -138436,19 +138362,12 @@ var PracticeModeController = () => {
138436
138362
  return;
138437
138363
  }
138438
138364
  try {
138439
- const totalQuestions = 10;
138440
- const numCodingQuestions = includeCoding ? numCoding : 0;
138441
- const standardQuestionTypes = ["multiple_choice", "multiple_response", "true_false", "fill_in_the_blanks", "short_answer", "numeric", "sequence"];
138442
- let selectedQuestionTypes = [...standardQuestionTypes];
138443
- if (includeCoding) {
138444
- selectedQuestionTypes.push("coding");
138445
- }
138446
138365
  const planInput = {
138447
- totalQuestions,
138448
- numCodingQuestions,
138366
+ totalQuestions: numQuestions,
138367
+ numCodingQuestions: selectedQuestionTypes.includes("coding") ? 1 : 0,
138368
+ // Simplified logic for now
138449
138369
  topics: selectedLOs.map((lo) => ({
138450
138370
  topic: lo.name || lo.code,
138451
- // FIX: Provide fallback for name
138452
138371
  ratio: 100 / selectedLOs.length,
138453
138372
  originalLoId: lo.code,
138454
138373
  originalSubject: lo.subject,
@@ -138561,26 +138480,17 @@ var PracticeModeController = () => {
138561
138480
  case "initializing":
138562
138481
  return /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col items-center justify-center min-h-[400px]" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-12 w-12 animate-spin text-primary" }), /* @__PURE__ */ React169__default.createElement("p", { className: "mt-4 text-muted-foreground" }, "Initializing Practice Mode..."));
138563
138482
  case "setup":
138564
- return /* @__PURE__ */ React169__default.createElement(
138565
- PracticeSetup,
138566
- {
138567
- onStartPractice: handleStartPractice,
138568
- isGenerating: false,
138569
- initialLOs: initialSuggestedLOs,
138570
- initialDifficulty: initialSuggestedDifficulty,
138571
- initialLanguage: initialSuggestedLanguage
138572
- }
138573
- );
138574
138483
  case "loading":
138575
138484
  return /* @__PURE__ */ React169__default.createElement(
138576
138485
  PracticeSetup,
138577
138486
  {
138578
- onStartPractice: () => {
138579
- },
138580
- isGenerating: true,
138487
+ onStartPractice: handleStartPractice,
138488
+ isGenerating: practiceState === "loading",
138581
138489
  initialLOs: initialSuggestedLOs,
138582
138490
  initialDifficulty: initialSuggestedDifficulty,
138583
- initialLanguage: initialSuggestedLanguage
138491
+ initialLanguage: initialSuggestedLanguage,
138492
+ maxQuestions,
138493
+ allowedQuestionTypes
138584
138494
  }
138585
138495
  );
138586
138496
  case "playing":
@@ -32,6 +32,8 @@ interface AIFullQuizGeneratorModalProps {
32
32
  onClose: () => void;
33
33
  onQuizGenerated: (questions: QuizQuestion[]) => void;
34
34
  language: string;
35
+ maxQuestions?: number;
36
+ allowedQuestionTypes?: QuestionTypeStrings[];
35
37
  }
36
38
  declare const AIFullQuizGeneratorModal: React__default.FC<AIFullQuizGeneratorModalProps>;
37
39
 
@@ -32,6 +32,8 @@ interface AIFullQuizGeneratorModalProps {
32
32
  onClose: () => void;
33
33
  onQuizGenerated: (questions: QuizQuestion[]) => void;
34
34
  language: string;
35
+ maxQuestions?: number;
36
+ allowedQuestionTypes?: QuestionTypeStrings[];
35
37
  }
36
38
  declare const AIFullQuizGeneratorModal: React__default.FC<AIFullQuizGeneratorModalProps>;
37
39
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thanh01.pmt/interactive-quiz-kit",
3
- "version": "1.0.75",
3
+ "version": "1.0.76",
4
4
  "description": "A comprehensive library for creating, managing, and playing interactive quizzes, with AI generation and SCORM support.",
5
5
  "keywords": [
6
6
  "react",