@thanh01.pmt/interactive-quiz-kit 1.0.75 → 1.0.77
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/authoring.cjs +16 -108
- package/dist/authoring.d.cts +1 -1
- package/dist/authoring.d.ts +1 -1
- package/dist/authoring.mjs +16 -108
- package/dist/react-ui.cjs +95 -185
- package/dist/react-ui.d.cts +9 -5
- package/dist/react-ui.d.ts +9 -5
- package/dist/react-ui.mjs +95 -185
- package/dist/{toaster-B2MPZYhi.d.ts → toaster-Bj78wmSz.d.ts} +2 -0
- package/dist/{toaster-Bvhmyef9.d.cts → toaster-CexT11VU.d.cts} +2 -0
- package/package.json +1 -1
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
|
|
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
|
-
|
|
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 >
|
|
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 >
|
|
102400
|
-
setError(
|
|
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 [
|
|
138266
|
-
const [
|
|
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 [
|
|
138274
|
-
const [
|
|
138199
|
+
const [numQuestions, setNumQuestions] = useState(10);
|
|
138200
|
+
const [selectedQuestionTypes, setSelectedQuestionTypes] = useState(new Set(allowedQuestionTypes));
|
|
138275
138201
|
useEffect(() => {
|
|
138276
|
-
const
|
|
138277
|
-
|
|
138278
|
-
|
|
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,
|
|
138251
|
+
onStartPractice(selectedLOs, selectedDifficulty, selectedLanguage, numQuestions, Array.from(selectedQuestionTypes));
|
|
138320
138252
|
}
|
|
138321
|
-
}, [onStartPractice, selectedDifficulty, selectedLanguage, selectedTopics,
|
|
138322
|
-
const
|
|
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 >
|
|
138326
|
-
|
|
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:
|
|
138335
|
-
|
|
138336
|
-
|
|
138337
|
-
|
|
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,
|
|
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