@thanh01.pmt/interactive-quiz-kit 1.0.74 → 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/authoring.cjs +29 -114
- package/dist/authoring.d.cts +1 -1
- package/dist/authoring.d.ts +1 -1
- package/dist/authoring.mjs +29 -114
- package/dist/react-ui.cjs +108 -191
- package/dist/react-ui.d.cts +9 -5
- package/dist/react-ui.d.ts +9 -5
- package/dist/react-ui.mjs +108 -191
- 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.cjs
CHANGED
|
@@ -102367,7 +102367,7 @@ var contextOptions = [
|
|
|
102367
102367
|
];
|
|
102368
102368
|
|
|
102369
102369
|
// src/react-ui/components/authoring/AIFullQuizGeneratorModal.tsx
|
|
102370
|
-
var
|
|
102370
|
+
var ALL_AVAILABLE_QUESTION_TYPES = [
|
|
102371
102371
|
{ value: "true_false", label: "True/False" },
|
|
102372
102372
|
{ value: "multiple_choice", label: "Multiple Choice" },
|
|
102373
102373
|
{ value: "multiple_response", label: "Multiple Response" },
|
|
@@ -102382,8 +102382,11 @@ var AIFullQuizGeneratorModal = ({
|
|
|
102382
102382
|
isOpen,
|
|
102383
102383
|
onClose,
|
|
102384
102384
|
onQuizGenerated,
|
|
102385
|
-
language: language3
|
|
102386
|
-
|
|
102385
|
+
language: language3,
|
|
102386
|
+
maxQuestions = 100,
|
|
102387
|
+
// Default value
|
|
102388
|
+
allowedQuestionTypes = ALL_AVAILABLE_QUESTION_TYPES.map((qt) => qt.value)
|
|
102389
|
+
// Default value
|
|
102387
102390
|
}) => {
|
|
102388
102391
|
const [totalQuestions, setTotalQuestions] = React169.useState(10);
|
|
102389
102392
|
const [topics, setTopics] = React169.useState([{ id: generateUniqueId("topic_"), topic: "", ratio: 100 }]);
|
|
@@ -102394,9 +102397,7 @@ var AIFullQuizGeneratorModal = ({
|
|
|
102394
102397
|
]);
|
|
102395
102398
|
const [selectedContextIds, setSelectedContextIds] = React169.useState([]);
|
|
102396
102399
|
const [customContextInput, setCustomContextInput] = React169.useState("");
|
|
102397
|
-
const [selectedQuestionTypes, setSelectedQuestionTypes] = React169.useState(
|
|
102398
|
-
availableQuestionTypesForFullQuiz.map((qt) => qt.value)
|
|
102399
|
-
);
|
|
102400
|
+
const [selectedQuestionTypes, setSelectedQuestionTypes] = React169.useState([]);
|
|
102400
102401
|
const [authorizeAISkipReview, setAuthorizeAISkipReview] = React169.useState(false);
|
|
102401
102402
|
const [isLoading, setIsLoading] = React169.useState(false);
|
|
102402
102403
|
const [error, setError] = React169.useState(null);
|
|
@@ -102405,6 +102406,9 @@ var AIFullQuizGeneratorModal = ({
|
|
|
102405
102406
|
const { toast: toast2 } = useToast();
|
|
102406
102407
|
const [isApiKeyManagerModalOpen, setIsApiKeyManagerModalOpen] = React169.useState(false);
|
|
102407
102408
|
const [geminiApiKeyExists, setGeminiApiKeyExists] = React169.useState(false);
|
|
102409
|
+
const availableQuestionTypesForFullQuiz = React169.useMemo(() => {
|
|
102410
|
+
return ALL_AVAILABLE_QUESTION_TYPES.filter((qt) => allowedQuestionTypes.includes(qt.value));
|
|
102411
|
+
}, [allowedQuestionTypes]);
|
|
102408
102412
|
React169.useEffect(() => {
|
|
102409
102413
|
if (isOpen) {
|
|
102410
102414
|
setTotalQuestions(10);
|
|
@@ -102424,7 +102428,7 @@ var AIFullQuizGeneratorModal = ({
|
|
|
102424
102428
|
setAiGeneratedPlan(null);
|
|
102425
102429
|
setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
|
|
102426
102430
|
}
|
|
102427
|
-
}, [isOpen]);
|
|
102431
|
+
}, [isOpen, availableQuestionTypesForFullQuiz]);
|
|
102428
102432
|
const handleApiKeyModalClose = () => {
|
|
102429
102433
|
setIsApiKeyManagerModalOpen(false);
|
|
102430
102434
|
setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
|
|
@@ -102432,7 +102436,7 @@ var AIFullQuizGeneratorModal = ({
|
|
|
102432
102436
|
const handleTotalQuestionsChange = (e3) => {
|
|
102433
102437
|
let num = parseInt(e3.target.value, 10);
|
|
102434
102438
|
if (isNaN(num) || num < 1) num = 1;
|
|
102435
|
-
if (num >
|
|
102439
|
+
if (num > maxQuestions) num = maxQuestions;
|
|
102436
102440
|
setTotalQuestions(num);
|
|
102437
102441
|
};
|
|
102438
102442
|
const handleTopicChange = (id3, field, value) => {
|
|
@@ -102462,8 +102466,8 @@ var AIFullQuizGeneratorModal = ({
|
|
|
102462
102466
|
setError("Gemini API Key is required for AI features. Please set it first.");
|
|
102463
102467
|
return false;
|
|
102464
102468
|
}
|
|
102465
|
-
if (totalQuestions <= 0 || totalQuestions >
|
|
102466
|
-
setError(
|
|
102469
|
+
if (totalQuestions <= 0 || totalQuestions > maxQuestions) {
|
|
102470
|
+
setError(`Total questions must be between 1 and ${maxQuestions}.`);
|
|
102467
102471
|
return false;
|
|
102468
102472
|
}
|
|
102469
102473
|
if (topics.some((t4) => !t4.topic.trim())) {
|
|
@@ -102502,7 +102506,6 @@ var AIFullQuizGeneratorModal = ({
|
|
|
102502
102506
|
const stage2Input = {
|
|
102503
102507
|
quizPlan: plan,
|
|
102504
102508
|
language: language3,
|
|
102505
|
-
// <-- SỬ DỤNG PROP
|
|
102506
102509
|
imageContexts: []
|
|
102507
102510
|
};
|
|
102508
102511
|
const stage2Output = await generateQuestionsFromQuizPlan(stage2Input, apiKey);
|
|
@@ -102553,7 +102556,6 @@ var AIFullQuizGeneratorModal = ({
|
|
|
102553
102556
|
try {
|
|
102554
102557
|
const planInput = {
|
|
102555
102558
|
language: language3,
|
|
102556
|
-
// <-- SỬ DỤNG PROP
|
|
102557
102559
|
totalQuestions,
|
|
102558
102560
|
numCodingQuestions: 0,
|
|
102559
102561
|
topics: topics.map((t4) => ({ topic: t4.topic, ratio: t4.ratio })),
|
|
@@ -102617,103 +102619,9 @@ var AIFullQuizGeneratorModal = ({
|
|
|
102617
102619
|
};
|
|
102618
102620
|
const renderContent3 = () => {
|
|
102619
102621
|
if (currentStage === "review") {
|
|
102620
|
-
return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("h3", { className: "text-lg font-Medium mb-2 text-primary flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Eye, { className: "mr-2 h-5 w-5" }), " Review & Adjust AI Generated Quiz Plan"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(ScrollArea2, { className: "max-h-[calc(60vh - 120px)] pr-3" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, aiGeneratedPlan.map((plannedQ, index3) => /* @__PURE__ */ React169__namespace.default.createElement(Card, { key: `planned-${index3}-${plannedQ.plannedTopic.replace(/\s/g, "")}`, className: "p-3" }, /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "p-0 space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold text-sm" }, "Q", index3 + 1, ": ", plannedQ.plannedTopic), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex space-x-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "up"), disabled: index3 === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(ArrowUp, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "down"), disabled: index3 === aiGeneratedPlan.length - 1 }, /* @__PURE__ */ React169__namespace.default.createElement(ArrowDown, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7 text-destructive", onClick: () => handleRemovePlannedQuestion(index3) }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-3 items-end" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `review-qtype-${index3}`, className: "text-xs" }, "Question Type"), /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102621
|
-
Select2,
|
|
102622
|
-
{
|
|
102623
|
-
value: plannedQ.plannedQuestionType,
|
|
102624
|
-
onValueChange: (value) => handleChangePlannedQuestionType(index3, value)
|
|
102625
|
-
},
|
|
102626
|
-
/* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: `review-qtype-${index3}`, className: "h-9 text-xs" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)),
|
|
102627
|
-
/* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: qType.value, value: qType.value, className: "text-xs" }, qType.label)))
|
|
102628
|
-
)), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "text-xs block" }, "Bloom Level"), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs p-2 h-9 border rounded-md bg-secondary/50 flex items-center capitalize" }, plannedQ.plannedBloomLevel)))))))) : /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, "No quiz plan generated or plan is empty."), error && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error));
|
|
102622
|
+
return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("h3", { className: "text-lg font-Medium mb-2 text-primary flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Eye, { className: "mr-2 h-5 w-5" }), " Review & Adjust AI Generated Quiz Plan"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(ScrollArea2, { className: "max-h-[calc(60vh - 120px)] pr-3" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, aiGeneratedPlan.map((plannedQ, index3) => /* @__PURE__ */ React169__namespace.default.createElement(Card, { key: `planned-${index3}-${plannedQ.plannedTopic.replace(/\s/g, "")}`, className: "p-3" }, /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "p-0 space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold text-sm" }, "Q", index3 + 1, ": ", plannedQ.plannedTopic), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex space-x-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "up"), disabled: index3 === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(ArrowUp, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "down"), disabled: index3 === aiGeneratedPlan.length - 1 }, /* @__PURE__ */ React169__namespace.default.createElement(ArrowDown, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7 text-destructive", onClick: () => handleRemovePlannedQuestion(index3) }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-3 items-end" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `review-qtype-${index3}`, className: "text-xs" }, "Question Type"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: plannedQ.plannedQuestionType, onValueChange: (value) => handleChangePlannedQuestionType(index3, value) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: `review-qtype-${index3}`, className: "h-9 text-xs" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: qType.value, value: qType.value, className: "text-xs" }, qType.label))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "text-xs block" }, "Bloom Level"), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs p-2 h-9 border rounded-md bg-secondary/50 flex items-center capitalize" }, plannedQ.plannedBloomLevel)))))))) : /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, "No quiz plan generated or plan is empty."), error && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error));
|
|
102629
102623
|
}
|
|
102630
|
-
return /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "max-h-[calc(70vh - 100px)] pl-4 pr-3 py-1" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 py-1 px-2" }, !geminiApiKeyExists && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-3 mb-4 border border-amber-500 bg-amber-50 rounded-md text-amber-700" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-start" }, /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "h-5 w-5 mr-2 mt-0.5 flex-shrink-0" }), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold" }, "Gemini API Key Required"), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs" }, "To use AI quiz generation, please set your Google Gemini API Key."), /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102631
|
-
Button,
|
|
102632
|
-
{
|
|
102633
|
-
variant: "link",
|
|
102634
|
-
className: "p-0 h-auto text-xs text-amber-700 hover:text-amber-800 mt-1",
|
|
102635
|
-
onClick: () => setIsApiKeyManagerModalOpen(true)
|
|
102636
|
-
},
|
|
102637
|
-
/* @__PURE__ */ React169__namespace.default.createElement(Settings, { className: "mr-1 h-3 w-3" }),
|
|
102638
|
-
" Set API Key Now"
|
|
102639
|
-
)))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "total-questions" }, "Total Number of Questions (1-100)"), /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102640
|
-
Input,
|
|
102641
|
-
{
|
|
102642
|
-
id: "total-questions",
|
|
102643
|
-
type: "number",
|
|
102644
|
-
value: totalQuestions,
|
|
102645
|
-
onChange: handleTotalQuestionsChange,
|
|
102646
|
-
min: "1",
|
|
102647
|
-
max: "100"
|
|
102648
|
-
}
|
|
102649
|
-
)), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Topic Distribution"), topics.map((topicItem, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: topicItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102650
|
-
Input,
|
|
102651
|
-
{
|
|
102652
|
-
type: "text",
|
|
102653
|
-
placeholder: `Topic ${index3 + 1}`,
|
|
102654
|
-
value: topicItem.topic,
|
|
102655
|
-
onChange: (e3) => handleTopicChange(topicItem.id, "topic", e3.target.value),
|
|
102656
|
-
className: "flex-grow"
|
|
102657
|
-
}
|
|
102658
|
-
), /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102659
|
-
Input,
|
|
102660
|
-
{
|
|
102661
|
-
type: "number",
|
|
102662
|
-
placeholder: "Ratio %",
|
|
102663
|
-
value: topicItem.ratio,
|
|
102664
|
-
onChange: (e3) => handleTopicChange(topicItem.id, "ratio", parseInt(e3.target.value, 10) || 0),
|
|
102665
|
-
className: "w-24",
|
|
102666
|
-
min: "0",
|
|
102667
|
-
max: "100"
|
|
102668
|
-
}
|
|
102669
|
-
), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeTopic(topicItem.id), disabled: topics.length <= 1 }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: addTopic }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic")), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Bloom Level Distribution"), bloomLevels.map((bloomItem) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: bloomItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "w-32 capitalize flex-shrink-0" }, bloomItem.level), /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102670
|
-
Input,
|
|
102671
|
-
{
|
|
102672
|
-
type: "number",
|
|
102673
|
-
placeholder: "Ratio %",
|
|
102674
|
-
value: bloomItem.ratio,
|
|
102675
|
-
onChange: (e3) => handleBloomRatioChange(bloomItem.id, parseInt(e3.target.value, 10) || 0),
|
|
102676
|
-
className: "w-24",
|
|
102677
|
-
min: "0",
|
|
102678
|
-
max: "100"
|
|
102679
|
-
}
|
|
102680
|
-
)))), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Relevant Contexts (Optional, Select Multiple)"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { key: opt.contextId, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102681
|
-
Checkbox2,
|
|
102682
|
-
{
|
|
102683
|
-
id: `ctx-full-${opt.contextId}`,
|
|
102684
|
-
checked: selectedContextIds.includes(opt.contextId),
|
|
102685
|
-
onCheckedChange: () => handleContextToggle(opt.contextId)
|
|
102686
|
-
}
|
|
102687
|
-
), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `ctx-full-${opt.contextId}`, className: "text-xs font-normal cursor-pointer" }, opt.contextDescription, " (", opt.shortContextId, ")")))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102688
|
-
Checkbox2,
|
|
102689
|
-
{
|
|
102690
|
-
id: `ctx-full-__custom__`,
|
|
102691
|
-
checked: selectedContextIds.includes("__custom__"),
|
|
102692
|
-
onCheckedChange: () => handleContextToggle("__custom__")
|
|
102693
|
-
}
|
|
102694
|
-
), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `ctx-full-__custom__`, className: "text-xs font-normal cursor-pointer ml-2" }, "Other (Custom Input)")), selectedContextIds.includes("__custom__") && /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102695
|
-
Textarea,
|
|
102696
|
-
{
|
|
102697
|
-
value: customContextInput,
|
|
102698
|
-
onChange: (e3) => setCustomContextInput(e3.target.value),
|
|
102699
|
-
placeholder: "Enter your specific custom context here...",
|
|
102700
|
-
className: "min-h-[60px] mt-2 text-sm"
|
|
102701
|
-
}
|
|
102702
|
-
)), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Question Types to Use (Select Multiple)"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: qType.value, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102703
|
-
Checkbox2,
|
|
102704
|
-
{
|
|
102705
|
-
id: `qtype-full-${qType.value}`,
|
|
102706
|
-
checked: selectedQuestionTypes.includes(qType.value),
|
|
102707
|
-
onCheckedChange: () => handleQuestionTypeToggle(qType.value)
|
|
102708
|
-
}
|
|
102709
|
-
), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `qtype-full-${qType.value}`, className: "text-xs font-normal cursor-pointer" }, qType.label))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-2 pt-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
|
|
102710
|
-
Checkbox2,
|
|
102711
|
-
{
|
|
102712
|
-
id: "authorize-ai-skip-review",
|
|
102713
|
-
checked: authorizeAISkipReview,
|
|
102714
|
-
onCheckedChange: (checked) => setAuthorizeAISkipReview(!!checked)
|
|
102715
|
-
}
|
|
102716
|
-
), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "authorize-ai-skip-review", className: "text-sm font-normal" }, "Authorize AI (Skip Review & Generate Directly)")), error && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error)));
|
|
102624
|
+
return /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "max-h-[calc(70vh - 100px)] pl-4 pr-3 py-1" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 py-1 px-2" }, !geminiApiKeyExists && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-3 mb-4 border border-amber-500 bg-amber-50 rounded-md text-amber-700" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-start" }, /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "h-5 w-5 mr-2 mt-0.5 flex-shrink-0" }), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold" }, "Gemini API Key Required"), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs" }, "To use AI quiz generation, please set your Google Gemini API Key."), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(Settings, { className: "mr-1 h-3 w-3" }), " Set API Key Now")))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "total-questions" }, "Total Number of Questions (1-", maxQuestions, ")"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "total-questions", type: "number", value: totalQuestions, onChange: handleTotalQuestionsChange, min: "1", max: maxQuestions })), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Topic Distribution"), topics.map((topicItem, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: topicItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__namespace.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__namespace.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__namespace.default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeTopic(topicItem.id), disabled: topics.length <= 1 }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: addTopic }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic")), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Bloom Level Distribution"), bloomLevels.map((bloomItem) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: bloomItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "w-32 capitalize flex-shrink-0" }, bloomItem.level), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Relevant Contexts (Optional, Select Multiple)"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { key: opt.contextId, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Checkbox2, { id: `ctx-full-${opt.contextId}`, checked: selectedContextIds.includes(opt.contextId), onCheckedChange: () => handleContextToggle(opt.contextId) }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `ctx-full-${opt.contextId}`, className: "text-xs font-normal cursor-pointer" }, opt.contextDescription, " (", opt.shortContextId, ")"))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "mt-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Checkbox2, { id: `ctx-full-__custom__`, checked: selectedContextIds.includes("__custom__"), onCheckedChange: () => handleContextToggle("__custom__") }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `ctx-full-__custom__`, className: "text-xs font-normal cursor-pointer ml-2" }, "Other (Custom Input)")), selectedContextIds.includes("__custom__") && /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Question Types to Use (Select Multiple)"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: qType.value, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Checkbox2, { id: `qtype-full-${qType.value}`, checked: selectedQuestionTypes.includes(qType.value), onCheckedChange: () => handleQuestionTypeToggle(qType.value) }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `qtype-full-${qType.value}`, className: "text-xs font-normal cursor-pointer" }, qType.label))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-2 pt-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Checkbox2, { id: "authorize-ai-skip-review", checked: authorizeAISkipReview, onCheckedChange: (checked) => setAuthorizeAISkipReview(!!checked) }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "authorize-ai-skip-review", className: "text-sm font-normal" }, "Authorize AI (Skip Review & Generate Directly)")), error && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error)));
|
|
102717
102625
|
};
|
|
102718
102626
|
return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => !open && onClose() }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-[650px] md:max-w-[750px] lg:max-w-[800px] max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, { className: "flex items-center font-headline text-2xl" }, /* @__PURE__ */ React169__namespace.default.createElement(FileText, { className: "mr-2 h-6 w-6 text-primary" }), " Generate Full Quiz with AI"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(DialogFooter, { className: "pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: onClose }, "Cancel")), currentStage === "input" && /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", onClick: handleSubmitPlanGeneration, disabled: isLoading || !geminiApiKeyExists }, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__namespace.default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), authorizeAISkipReview ? "Generate Quiz Directly" : "Generate Quiz Plan"), currentStage === "review" && !isLoading && /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", onClick: handleGenerateQuestionsFromReviewedPlan, disabled: isLoading || !aiGeneratedPlan || aiGeneratedPlan.length === 0 || !geminiApiKeyExists }, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__namespace.default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), "Generate Questions from Plan"), currentStage === "generating" && isLoading && /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", disabled: true }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Generating... Please Wait")))), /* @__PURE__ */ React169__namespace.default.createElement(APIKeyManagerModal, { isOpen: isApiKeyManagerModalOpen, onClose: handleApiKeyModalClose }));
|
|
102719
102627
|
};
|
|
@@ -138319,58 +138227,74 @@ var AVAILABLE_LANGUAGES = [
|
|
|
138319
138227
|
{ value: "Vietnamese", label: "Ti\u1EBFng Vi\u1EC7t" },
|
|
138320
138228
|
{ value: "English", label: "English" }
|
|
138321
138229
|
];
|
|
138230
|
+
var ALL_QUESTION_TYPE_LABELS = {
|
|
138231
|
+
multiple_choice: "Multiple Choice",
|
|
138232
|
+
multiple_response: "Multiple Response",
|
|
138233
|
+
true_false: "True/False",
|
|
138234
|
+
short_answer: "Short Answer",
|
|
138235
|
+
numeric: "Numeric",
|
|
138236
|
+
fill_in_the_blanks: "Fill in the Blanks",
|
|
138237
|
+
sequence: "Sequence",
|
|
138238
|
+
matching: "Matching",
|
|
138239
|
+
drag_and_drop: "Drag & Drop",
|
|
138240
|
+
hotspot: "Hotspot",
|
|
138241
|
+
blockly_programming: "Blockly",
|
|
138242
|
+
scratch_programming: "Scratch",
|
|
138243
|
+
coding: "Coding"
|
|
138244
|
+
};
|
|
138322
138245
|
var PracticeSetup = ({
|
|
138323
138246
|
onStartPractice,
|
|
138324
138247
|
isGenerating,
|
|
138325
138248
|
initialLOs,
|
|
138326
138249
|
initialDifficulty,
|
|
138327
|
-
initialLanguage
|
|
138250
|
+
initialLanguage,
|
|
138251
|
+
maxQuestions = 20,
|
|
138252
|
+
// Default max questions
|
|
138253
|
+
allowedQuestionTypes = ["true_false", "multiple_choice", "multiple_response"]
|
|
138254
|
+
// Default allowed types
|
|
138328
138255
|
}) => {
|
|
138329
138256
|
const { t: t4 } = useTranslation();
|
|
138330
138257
|
const [subjects, setSubjects] = React169.useState([]);
|
|
138331
|
-
const [
|
|
138332
|
-
const [
|
|
138258
|
+
const [categoriesBySubject, setCategoriesBySubject] = React169.useState({});
|
|
138259
|
+
const [topicsByCategory, setTopicsByCategory] = React169.useState({});
|
|
138333
138260
|
const [hasData, setHasData] = React169.useState(false);
|
|
138334
138261
|
const [selectedSubject, setSelectedSubject] = React169.useState("");
|
|
138335
|
-
const [selectedCategory, setSelectedCategory] = React169.useState("");
|
|
138336
138262
|
const [selectedTopics, setSelectedTopics] = React169.useState(/* @__PURE__ */ new Set());
|
|
138337
138263
|
const [selectedLanguage, setSelectedLanguage] = React169.useState(AVAILABLE_LANGUAGES[0].value);
|
|
138338
138264
|
const [selectedDifficulty, setSelectedDifficulty] = React169.useState("");
|
|
138339
|
-
const [
|
|
138340
|
-
const [
|
|
138265
|
+
const [numQuestions, setNumQuestions] = React169.useState(10);
|
|
138266
|
+
const [selectedQuestionTypes, setSelectedQuestionTypes] = React169.useState(new Set(allowedQuestionTypes));
|
|
138341
138267
|
React169.useEffect(() => {
|
|
138342
|
-
const
|
|
138343
|
-
|
|
138344
|
-
|
|
138268
|
+
const allLOs = TopicDataService.getData();
|
|
138269
|
+
if (allLOs.length > 0) {
|
|
138270
|
+
setHasData(true);
|
|
138271
|
+
const uniqueSubjects = [...new Set(allLOs.map((lo) => lo.subject))].sort();
|
|
138272
|
+
setSubjects(uniqueSubjects);
|
|
138273
|
+
const catsBySub = {};
|
|
138274
|
+
uniqueSubjects.forEach((sub) => {
|
|
138275
|
+
catsBySub[sub] = [...new Set(allLOs.filter((lo) => lo.subject === sub).map((lo) => lo.category))].sort();
|
|
138276
|
+
});
|
|
138277
|
+
setCategoriesBySubject(catsBySub);
|
|
138278
|
+
const topsByCat = {};
|
|
138279
|
+
allLOs.forEach((lo) => {
|
|
138280
|
+
if (!topsByCat[lo.category]) {
|
|
138281
|
+
topsByCat[lo.category] = [];
|
|
138282
|
+
}
|
|
138283
|
+
topsByCat[lo.category].push(lo.topic);
|
|
138284
|
+
});
|
|
138285
|
+
Object.keys(topsByCat).forEach((cat) => {
|
|
138286
|
+
topsByCat[cat] = [...new Set(topsByCat[cat])].sort();
|
|
138287
|
+
});
|
|
138288
|
+
setTopicsByCategory(topsByCat);
|
|
138289
|
+
}
|
|
138345
138290
|
if (initialLOs && initialLOs.length > 0) {
|
|
138346
138291
|
const firstLO = initialLOs[0];
|
|
138347
138292
|
setSelectedSubject(firstLO.subject);
|
|
138348
|
-
setSelectedCategory(firstLO.category);
|
|
138349
138293
|
setSelectedTopics(new Set(initialLOs.map((lo) => lo.topic)));
|
|
138350
138294
|
setSelectedDifficulty(initialDifficulty || "Medium");
|
|
138351
138295
|
setSelectedLanguage(initialLanguage || AVAILABLE_LANGUAGES[0].value);
|
|
138352
138296
|
}
|
|
138353
138297
|
}, [initialLOs, initialDifficulty, initialLanguage]);
|
|
138354
|
-
React169.useEffect(() => {
|
|
138355
|
-
if (selectedSubject) {
|
|
138356
|
-
const filteredCategories = TopicDataService.getCategoriesBySubject(selectedSubject);
|
|
138357
|
-
setCategories(filteredCategories);
|
|
138358
|
-
if (!initialLOs) {
|
|
138359
|
-
setSelectedCategory("");
|
|
138360
|
-
setTopics([]);
|
|
138361
|
-
setSelectedTopics(/* @__PURE__ */ new Set());
|
|
138362
|
-
}
|
|
138363
|
-
}
|
|
138364
|
-
}, [selectedSubject, initialLOs]);
|
|
138365
|
-
React169.useEffect(() => {
|
|
138366
|
-
if (selectedCategory) {
|
|
138367
|
-
const filteredTopics = TopicDataService.getTopicsByCategory(selectedCategory);
|
|
138368
|
-
setTopics(filteredTopics);
|
|
138369
|
-
if (!initialLOs) {
|
|
138370
|
-
setSelectedTopics(/* @__PURE__ */ new Set());
|
|
138371
|
-
}
|
|
138372
|
-
}
|
|
138373
|
-
}, [selectedCategory, initialLOs]);
|
|
138374
138298
|
const handleTopicToggle = React169.useCallback((topic, checked) => {
|
|
138375
138299
|
setSelectedTopics((prevSelected) => {
|
|
138376
138300
|
const newSelected = new Set(prevSelected);
|
|
@@ -138379,37 +138303,36 @@ var PracticeSetup = ({
|
|
|
138379
138303
|
return newSelected;
|
|
138380
138304
|
});
|
|
138381
138305
|
}, []);
|
|
138306
|
+
const handleQuestionTypeToggle = React169.useCallback((type, checked) => {
|
|
138307
|
+
setSelectedQuestionTypes((prev) => {
|
|
138308
|
+
const newSet = new Set(prev);
|
|
138309
|
+
if (checked) newSet.add(type);
|
|
138310
|
+
else newSet.delete(type);
|
|
138311
|
+
return newSet;
|
|
138312
|
+
});
|
|
138313
|
+
}, []);
|
|
138382
138314
|
const handleStartClick = React169.useCallback(() => {
|
|
138383
|
-
if (selectedTopics.size > 0 && selectedDifficulty && selectedLanguage) {
|
|
138315
|
+
if (selectedTopics.size > 0 && selectedDifficulty && selectedLanguage && numQuestions > 0 && selectedQuestionTypes.size > 0) {
|
|
138384
138316
|
const selectedLOs = TopicDataService.getLearningObjectivesByTopics(Array.from(selectedTopics));
|
|
138385
|
-
onStartPractice(selectedLOs, selectedDifficulty, selectedLanguage,
|
|
138317
|
+
onStartPractice(selectedLOs, selectedDifficulty, selectedLanguage, numQuestions, Array.from(selectedQuestionTypes));
|
|
138386
138318
|
}
|
|
138387
|
-
}, [onStartPractice, selectedDifficulty, selectedLanguage, selectedTopics,
|
|
138388
|
-
const
|
|
138319
|
+
}, [onStartPractice, selectedDifficulty, selectedLanguage, selectedTopics, numQuestions, selectedQuestionTypes]);
|
|
138320
|
+
const handleNumQuestionsChange = (e3) => {
|
|
138389
138321
|
let value = parseInt(e3.target.value, 10);
|
|
138390
138322
|
if (isNaN(value) || value < 1) value = 1;
|
|
138391
|
-
if (value >
|
|
138392
|
-
|
|
138323
|
+
if (value > maxQuestions) value = maxQuestions;
|
|
138324
|
+
setNumQuestions(value);
|
|
138393
138325
|
};
|
|
138394
138326
|
const isReadyToStart = React169.useMemo(() => {
|
|
138395
|
-
return selectedTopics.size > 0 && !!selectedDifficulty && !!selectedLanguage && !isGenerating;
|
|
138396
|
-
}, [selectedTopics, selectedDifficulty, selectedLanguage, isGenerating]);
|
|
138327
|
+
return selectedTopics.size > 0 && !!selectedDifficulty && !!selectedLanguage && numQuestions > 0 && selectedQuestionTypes.size > 0 && !isGenerating;
|
|
138328
|
+
}, [selectedTopics, selectedDifficulty, selectedLanguage, numQuestions, selectedQuestionTypes, isGenerating]);
|
|
138397
138329
|
if (!hasData) {
|
|
138398
138330
|
return /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "w-full max-w-2xl mx-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex items-center text-2xl font-headline" }, /* @__PURE__ */ React169__namespace.default.createElement(BrainCircuit, { className: "mr-3 h-6 w-6 text-primary" }), t4("practiceFlow.setup.title"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleAlert, { className: "h-4 w-4" }), /* @__PURE__ */ React169__namespace.default.createElement(AlertTitle, null, "No Learning Objective Data Found"), /* @__PURE__ */ React169__namespace.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.'))));
|
|
138399
138331
|
}
|
|
138400
|
-
return /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "w-full max-w-2xl mx-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex items-center text-2xl font-headline" }, /* @__PURE__ */ React169__namespace.default.createElement(BrainCircuit, { className: "mr-3 h-6 w-6 text-primary" }), t4("practiceFlow.setup.title")), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, null, t4("practiceFlow.setup.description"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subject-select", className: "font-semibold" }, t4("practiceFlow.setup.step1_title")), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedSubject, onValueChange:
|
|
138401
|
-
|
|
138402
|
-
|
|
138403
|
-
|
|
138404
|
-
type: "number",
|
|
138405
|
-
value: numCodingQuestions,
|
|
138406
|
-
onChange: handleNumCodingChange,
|
|
138407
|
-
min: "1",
|
|
138408
|
-
max: "3",
|
|
138409
|
-
className: "w-24 mt-1",
|
|
138410
|
-
disabled: isGenerating
|
|
138411
|
-
}
|
|
138412
|
-
))))), /* @__PURE__ */ React169__namespace.default.createElement(CardFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleStartClick, disabled: !isReadyToStart, className: "w-full" }, isGenerating ? /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t4("practiceFlow.setup.generatingButton")) : /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), t4("practiceFlow.setup.startButton")))));
|
|
138332
|
+
return /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "w-full max-w-2xl mx-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex items-center text-2xl font-headline" }, /* @__PURE__ */ React169__namespace.default.createElement(BrainCircuit, { className: "mr-3 h-6 w-6 text-primary" }), t4("practiceFlow.setup.title")), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, null, t4("practiceFlow.setup.description"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subject-select", className: "font-semibold" }, t4("practiceFlow.setup.step1_title")), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedSubject, onValueChange: (val) => {
|
|
138333
|
+
setSelectedSubject(val);
|
|
138334
|
+
setSelectedTopics(/* @__PURE__ */ new Set());
|
|
138335
|
+
}, disabled: isGenerating }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "subject-select" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("practiceFlow.setup.step1_placeholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, subjects.map((subject) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: subject, value: subject }, subject))))), selectedSubject && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 animate-in fade-in duration-300" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, t4("practiceFlow.setup.step2_title_combined")), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-48 w-full rounded-md border p-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Accordion2, { type: "multiple", className: "w-full" }, (categoriesBySubject[selectedSubject] || []).map((category) => /* @__PURE__ */ React169__namespace.default.createElement(AccordionItem2, { value: category, key: category }, /* @__PURE__ */ React169__namespace.default.createElement(AccordionTrigger2, { className: "text-sm font-medium px-2" }, category), /* @__PURE__ */ React169__namespace.default.createElement(AccordionContent2, { className: "pl-4" }, (topicsByCategory[category] || []).map((topic) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: topic, className: "flex items-center space-x-2 py-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Checkbox2, { id: `topic-${topic.replace(/\s/g, "-")}`, checked: selectedTopics.has(topic), onCheckedChange: (checked) => handleTopicToggle(topic, !!checked), disabled: isGenerating }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `topic-${topic.replace(/\s/g, "-")}`, className: "font-normal cursor-pointer" }, topic))))))))), selectedTopics.size > 0 && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-lg animate-in fade-in duration-700" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "num-questions", className: "font-semibold" }, t4("practiceFlow.setup.step_num_questions", { max: maxQuestions })), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "num-questions", type: "number", value: numQuestions, onChange: handleNumQuestionsChange, min: "1", max: maxQuestions, disabled: isGenerating })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "difficulty-select", className: "font-semibold" }, t4("practiceFlow.setup.step5_title")), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedDifficulty, onValueChange: (v) => setSelectedDifficulty(v), disabled: isGenerating }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "difficulty-select" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("practiceFlow.setup.step5_placeholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, DIFFICULTY_LEVELS.map((level) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: level, value: level }, level)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, t4("practiceFlow.setup.step_question_types")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-2" }, allowedQuestionTypes.map((type) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: type, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Checkbox2, { id: `qtype-${type}`, checked: selectedQuestionTypes.has(type), onCheckedChange: (checked) => handleQuestionTypeToggle(type, !!checked), disabled: isGenerating }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `qtype-${type}`, className: "font-normal cursor-pointer text-sm" }, ALL_QUESTION_TYPE_LABELS[type] || type))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "language-select", className: "font-semibold" }, t4("practiceFlow.setup.step4_title")), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedLanguage, onValueChange: setSelectedLanguage, disabled: isGenerating }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "language-select" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("practiceFlow.setup.step4_placeholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, AVAILABLE_LANGUAGES.map((lang) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: lang.value, value: lang.value }, lang.label))))))), /* @__PURE__ */ React169__namespace.default.createElement(CardFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleStartClick, disabled: !isReadyToStart, className: "w-full" }, isGenerating ? /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), t4("practiceFlow.setup.generatingButton")) : /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), t4("practiceFlow.setup.startButton")))));
|
|
138413
138336
|
};
|
|
138414
138337
|
|
|
138415
138338
|
// src/react-ui/components/practice/PracticeModeController.tsx
|
|
@@ -138429,7 +138352,10 @@ var mapDifficultyToBloomDistribution = (difficulty) => {
|
|
|
138429
138352
|
return [{ level: "remembering", ratio: 50 }, { level: "understanding", ratio: 50 }];
|
|
138430
138353
|
}
|
|
138431
138354
|
};
|
|
138432
|
-
var PracticeModeController = (
|
|
138355
|
+
var PracticeModeController = ({
|
|
138356
|
+
maxQuestions,
|
|
138357
|
+
allowedQuestionTypes
|
|
138358
|
+
}) => {
|
|
138433
138359
|
const router = navigation.useRouter();
|
|
138434
138360
|
const [practiceState, setPracticeState] = React169.useState("initializing");
|
|
138435
138361
|
const [quizConfig, setQuizConfig] = React169.useState(null);
|
|
@@ -138490,7 +138416,7 @@ var PracticeModeController = () => {
|
|
|
138490
138416
|
setPracticeState("setup");
|
|
138491
138417
|
}
|
|
138492
138418
|
}, [toast2]);
|
|
138493
|
-
const handleStartPractice = React169.useCallback(async (selectedLOs, difficulty, language3,
|
|
138419
|
+
const handleStartPractice = React169.useCallback(async (selectedLOs, difficulty, language3, numQuestions, selectedQuestionTypes) => {
|
|
138494
138420
|
setPracticeState("loading");
|
|
138495
138421
|
setErrorMessage("");
|
|
138496
138422
|
const apiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
|
|
@@ -138502,19 +138428,12 @@ var PracticeModeController = () => {
|
|
|
138502
138428
|
return;
|
|
138503
138429
|
}
|
|
138504
138430
|
try {
|
|
138505
|
-
const totalQuestions = 10;
|
|
138506
|
-
const numCodingQuestions = includeCoding ? numCoding : 0;
|
|
138507
|
-
const standardQuestionTypes = ["multiple_choice", "multiple_response", "true_false", "fill_in_the_blanks", "short_answer", "numeric", "sequence"];
|
|
138508
|
-
let selectedQuestionTypes = [...standardQuestionTypes];
|
|
138509
|
-
if (includeCoding) {
|
|
138510
|
-
selectedQuestionTypes.push("coding");
|
|
138511
|
-
}
|
|
138512
138431
|
const planInput = {
|
|
138513
|
-
totalQuestions,
|
|
138514
|
-
numCodingQuestions,
|
|
138432
|
+
totalQuestions: numQuestions,
|
|
138433
|
+
numCodingQuestions: selectedQuestionTypes.includes("coding") ? 1 : 0,
|
|
138434
|
+
// Simplified logic for now
|
|
138515
138435
|
topics: selectedLOs.map((lo) => ({
|
|
138516
138436
|
topic: lo.name || lo.code,
|
|
138517
|
-
// FIX: Provide fallback for name
|
|
138518
138437
|
ratio: 100 / selectedLOs.length,
|
|
138519
138438
|
originalLoId: lo.code,
|
|
138520
138439
|
originalSubject: lo.subject,
|
|
@@ -138627,26 +138546,17 @@ var PracticeModeController = () => {
|
|
|
138627
138546
|
case "initializing":
|
|
138628
138547
|
return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col items-center justify-center min-h-[400px]" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-12 w-12 animate-spin text-primary" }), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "mt-4 text-muted-foreground" }, "Initializing Practice Mode..."));
|
|
138629
138548
|
case "setup":
|
|
138630
|
-
return /* @__PURE__ */ React169__namespace.default.createElement(
|
|
138631
|
-
PracticeSetup,
|
|
138632
|
-
{
|
|
138633
|
-
onStartPractice: handleStartPractice,
|
|
138634
|
-
isGenerating: false,
|
|
138635
|
-
initialLOs: initialSuggestedLOs,
|
|
138636
|
-
initialDifficulty: initialSuggestedDifficulty,
|
|
138637
|
-
initialLanguage: initialSuggestedLanguage
|
|
138638
|
-
}
|
|
138639
|
-
);
|
|
138640
138549
|
case "loading":
|
|
138641
138550
|
return /* @__PURE__ */ React169__namespace.default.createElement(
|
|
138642
138551
|
PracticeSetup,
|
|
138643
138552
|
{
|
|
138644
|
-
onStartPractice:
|
|
138645
|
-
|
|
138646
|
-
isGenerating: true,
|
|
138553
|
+
onStartPractice: handleStartPractice,
|
|
138554
|
+
isGenerating: practiceState === "loading",
|
|
138647
138555
|
initialLOs: initialSuggestedLOs,
|
|
138648
138556
|
initialDifficulty: initialSuggestedDifficulty,
|
|
138649
|
-
initialLanguage: initialSuggestedLanguage
|
|
138557
|
+
initialLanguage: initialSuggestedLanguage,
|
|
138558
|
+
maxQuestions,
|
|
138559
|
+
allowedQuestionTypes
|
|
138650
138560
|
}
|
|
138651
138561
|
);
|
|
138652
138562
|
case "playing":
|
|
@@ -140184,7 +140094,7 @@ function ApproachManager({
|
|
|
140184
140094
|
};
|
|
140185
140095
|
const handleEditItem = (item) => {
|
|
140186
140096
|
setCurrentItem(item);
|
|
140187
|
-
setFormState(item);
|
|
140097
|
+
setFormState({ ...item, difficulty: Array.isArray(item.difficulty) ? item.difficulty : [item.difficulty] });
|
|
140188
140098
|
setIsDialogOpen(true);
|
|
140189
140099
|
};
|
|
140190
140100
|
const handleDeleteItem = (item) => {
|
|
@@ -140277,11 +140187,18 @@ function ApproachManager({
|
|
|
140277
140187
|
await onBulkAdd(validatedRecords);
|
|
140278
140188
|
}
|
|
140279
140189
|
};
|
|
140280
|
-
return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Settings2, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Approaches"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(MetadataImportControls, { metadataName: "Approaches", onImport: handleImport }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Approach")))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Approaches found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Approach ID"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Verb (VI)"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Cognitive Level"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.verbVi), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.bloomLevelCode), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Approach" : "Add New Approach")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "code" }, "Approach Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "code", value: formState.code || "", onChange: (e3) => setFormState((s4) => ({ ...s4, code: e3.target.value.toUpperCase() })), placeholder: "e.g., REM-FAC-IDT-MCQ", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "name" }, "Name / Description"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "name", value: formState.name || "", onChange: (e3) => setFormState((s4) => ({ ...s4, name: e3.target.value })), placeholder: "e.g., Identify a Fact" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "verbEn" }, "Verb (English)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "verbEn", value: formState.verbEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, verbEn: e3.target.value })), placeholder: "e.g., Identify" })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "verbVi" }, "Verb (Vietnamese)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "verbVi", value: formState.verbVi || "", onChange: (e3) => setFormState((s4) => ({ ...s4, verbVi: e3.target.value })), placeholder: "e.g., Nh\u1EADn d\u1EA1ng" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "bloomLevelCode" }, "Cognitive Level"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.bloomLevelCode, onValueChange: (v) => setFormState((s4) => ({ ...s4, bloomLevelCode: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, bloomLevels.map((level) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: level.code, value: level.code }, level.name))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "knowledgeDimension" }, "Knowledge Dimension"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.knowledgeDimension, onValueChange: (v) => setFormState((s4) => ({ ...s4, knowledgeDimension: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, knowledgeDimensions.map((kd) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: kd, value: kd }, kd))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "iSpringQuizType" }, "iSpring Quiz Type"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.iSpringQuizType, onValueChange: (v) => setFormState((s4) => ({ ...s4, iSpringQuizType: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, questionTypes.map((qt) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: qt.code, value: qt.code }, qt.name)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Difficulty"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-4 pt-2" }, standardDifficulties.map((diff2) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: diff2, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
|
|
140281
|
-
|
|
140282
|
-
|
|
140283
|
-
|
|
140284
|
-
|
|
140190
|
+
return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Settings2, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Approaches"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(MetadataImportControls, { metadataName: "Approaches", onImport: handleImport }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Approach")))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Approaches found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Approach ID"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Verb (VI)"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Cognitive Level"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.verbVi), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.bloomLevelCode), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Approach" : "Add New Approach")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "code" }, "Approach Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "code", value: formState.code || "", onChange: (e3) => setFormState((s4) => ({ ...s4, code: e3.target.value.toUpperCase() })), placeholder: "e.g., REM-FAC-IDT-MCQ", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "name" }, "Name / Description"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "name", value: formState.name || "", onChange: (e3) => setFormState((s4) => ({ ...s4, name: e3.target.value })), placeholder: "e.g., Identify a Fact" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "verbEn" }, "Verb (English)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "verbEn", value: formState.verbEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, verbEn: e3.target.value })), placeholder: "e.g., Identify" })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "verbVi" }, "Verb (Vietnamese)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "verbVi", value: formState.verbVi || "", onChange: (e3) => setFormState((s4) => ({ ...s4, verbVi: e3.target.value })), placeholder: "e.g., Nh\u1EADn d\u1EA1ng" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "bloomLevelCode" }, "Cognitive Level"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.bloomLevelCode, onValueChange: (v) => setFormState((s4) => ({ ...s4, bloomLevelCode: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, bloomLevels.map((level) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: level.code, value: level.code }, level.name))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "knowledgeDimension" }, "Knowledge Dimension"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.knowledgeDimension, onValueChange: (v) => setFormState((s4) => ({ ...s4, knowledgeDimension: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, knowledgeDimensions.map((kd) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: kd, value: kd }, kd))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "iSpringQuizType" }, "iSpring Quiz Type"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.iSpringQuizType, onValueChange: (v) => setFormState((s4) => ({ ...s4, iSpringQuizType: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, questionTypes.map((qt) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: qt.code, value: qt.code }, qt.name)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Difficulty"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-4 pt-2" }, standardDifficulties.map((diff2) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: diff2, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
|
|
140191
|
+
Checkbox2,
|
|
140192
|
+
{
|
|
140193
|
+
id: `diff-${diff2}`,
|
|
140194
|
+
checked: (formState.difficulty || []).includes(diff2),
|
|
140195
|
+
onCheckedChange: (checked) => {
|
|
140196
|
+
const current = formState.difficulty || [];
|
|
140197
|
+
const newDiff = checked ? [...current, diff2] : current.filter((d) => d !== diff2);
|
|
140198
|
+
setFormState((s4) => ({ ...s4, difficulty: newDiff }));
|
|
140199
|
+
}
|
|
140200
|
+
}
|
|
140201
|
+
), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `diff-${diff2}` }, diff2))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "suggestContext" }, "Suggest Context (comma-separated codes)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "suggestContext", value: formState.suggestContext || "", onChange: (e3) => setFormState((s4) => ({ ...s4, suggestContext: e3.target.value })), placeholder: "e.g., A, B, D, G, H" })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "exampleEn" }, "Example (English)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "exampleEn", value: formState.exampleEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, exampleEn: e3.target.value })), placeholder: "English example prompt..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "exampleVi" }, "Example (Vietnamese)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "exampleVi", value: formState.exampleVi || "", onChange: (e3) => setFormState((s4) => ({ ...s4, exampleVi: e3.target.value })), placeholder: "Vietnamese example prompt..." }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.code, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
|
|
140285
140202
|
}
|
|
140286
140203
|
|
|
140287
140204
|
// src/react-ui/components/metadata/MetadataTabs.tsx
|