@thanh01.pmt/interactive-quiz-kit 1.0.72 → 1.0.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react-ui.cjs CHANGED
@@ -98269,21 +98269,30 @@ var EditableCombobox = ({
98269
98269
  disabled = false
98270
98270
  }) => {
98271
98271
  const [open, setOpen] = React169__namespace.useState(false);
98272
- const [inputValue, setInputValue] = React169__namespace.useState(value);
98272
+ const [inputValue, setInputValue] = React169__namespace.useState("");
98273
98273
  React169__namespace.useEffect(() => {
98274
- setInputValue(value);
98275
- }, [value]);
98276
- const handleSelect = (currentValue) => {
98277
- const newValue = currentValue === value ? "" : currentValue;
98278
- onChange(newValue);
98279
- setInputValue(newValue);
98274
+ if (value) {
98275
+ const selectedOption = options.find((option) => option.value.toLowerCase() === value.toLowerCase());
98276
+ setInputValue(selectedOption ? selectedOption.label : value);
98277
+ } else {
98278
+ setInputValue("");
98279
+ }
98280
+ }, [value, options, open]);
98281
+ const handleSelect = (selectedValue) => {
98282
+ onChange(selectedValue);
98280
98283
  setOpen(false);
98281
98284
  };
98282
- const handleBlur = () => {
98283
- onChange(inputValue);
98285
+ const handleOpenChange = (isOpen) => {
98286
+ if (!isOpen) {
98287
+ const match2 = options.find((option) => option.label.toLowerCase() === inputValue.toLowerCase());
98288
+ if (!match2 && inputValue !== (options.find((opt) => opt.value === value)?.label || value)) {
98289
+ onChange(inputValue);
98290
+ }
98291
+ }
98292
+ setOpen(isOpen);
98284
98293
  };
98285
98294
  const displayLabel = options.find((option) => option.value.toLowerCase() === value?.toLowerCase())?.label || value;
98286
- return /* @__PURE__ */ React169__namespace.createElement(Popover2, { open, onOpenChange: setOpen }, /* @__PURE__ */ React169__namespace.createElement(PopoverTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.createElement(
98295
+ return /* @__PURE__ */ React169__namespace.createElement(Popover2, { open, onOpenChange: handleOpenChange }, /* @__PURE__ */ React169__namespace.createElement(PopoverTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.createElement(
98287
98296
  Button,
98288
98297
  {
98289
98298
  variant: "outline",
@@ -98299,8 +98308,7 @@ var EditableCombobox = ({
98299
98308
  {
98300
98309
  placeholder: searchPlaceholder,
98301
98310
  value: inputValue,
98302
- onValueChange: setInputValue,
98303
- onBlur: handleBlur
98311
+ onValueChange: setInputValue
98304
98312
  }
98305
98313
  ), /* @__PURE__ */ React169__namespace.createElement(CommandList, null, /* @__PURE__ */ React169__namespace.createElement(CommandEmpty, null, noResultsMessage), /* @__PURE__ */ React169__namespace.createElement(CommandGroup, null, options.map((option) => /* @__PURE__ */ React169__namespace.createElement(
98306
98314
  CommandItem,
@@ -101237,6 +101245,10 @@ var supportedQuestionTypesForAI = [
101237
101245
  { value: "sequence", label: "Sequence" },
101238
101246
  { value: "matching", label: "Matching" }
101239
101247
  ];
101248
+ var availableLanguages = [
101249
+ { value: "English", label: "English" },
101250
+ { value: "Vietnamese", label: "Ti\u1EBFng Vi\u1EC7t" }
101251
+ ];
101240
101252
  var AIQuestionGeneratorModal = ({
101241
101253
  isOpen,
101242
101254
  onClose,
@@ -101246,51 +101258,65 @@ var AIQuestionGeneratorModal = ({
101246
101258
  subjects = [],
101247
101259
  topics = [],
101248
101260
  gradeLevels = [],
101249
- bloomLevels = []
101261
+ bloomLevels = [],
101262
+ categories = []
101250
101263
  }) => {
101251
- const [prompt, setPrompt] = React169.useState("");
101264
+ const [additionalInfo, setAdditionalInfo] = React169.useState("");
101252
101265
  const [isLoading, setIsLoading] = React169.useState(false);
101253
101266
  const [error, setError] = React169.useState(null);
101254
101267
  const { toast: toast2 } = useToast();
101255
101268
  const [subjectCode, setSubjectCode] = React169.useState("");
101269
+ const [categoryCode, setCategoryCode] = React169.useState("");
101256
101270
  const [topicCode, setTopicCode] = React169.useState("");
101257
101271
  const [gradeBand, setGradeBand] = React169.useState("");
101258
101272
  const [bloomLevelCode, setBloomLevelCode] = React169.useState("");
101259
101273
  const [selectedQuestionType, setSelectedQuestionType] = React169.useState("multiple_choice");
101274
+ const [selectedLanguage, setSelectedLanguage] = React169.useState(language3);
101260
101275
  const [numberOfOptions, setNumberOfOptions] = React169.useState(4);
101261
- const [minCorrectAnswers, setMinCorrectAnswers] = React169.useState(2);
101262
- const [maxCorrectAnswers, setMaxCorrectAnswers] = React169.useState(3);
101263
- const [isCaseSensitive, setIsCaseSensitive] = React169.useState(false);
101264
- const [numberOfBlanks, setNumberOfBlanks] = React169.useState(2);
101265
- const [numberOfSequenceItems, setNumberOfSequenceItems] = React169.useState(4);
101266
- const [numberOfMatchingPairs, setNumberOfMatchingPairs] = React169.useState(4);
101267
101276
  const [isApiKeyManagerModalOpen, setIsApiKeyManagerModalOpen] = React169.useState(false);
101268
101277
  const [geminiApiKeyExists, setGeminiApiKeyExists] = React169.useState(false);
101269
101278
  const finalQuestionType = questionTypeProp || selectedQuestionType;
101270
101279
  React169.useEffect(() => {
101271
101280
  if (isOpen) {
101272
- setPrompt("");
101281
+ setAdditionalInfo("");
101273
101282
  setError(null);
101274
101283
  setIsLoading(false);
101275
101284
  setSubjectCode("");
101285
+ setCategoryCode("");
101276
101286
  setTopicCode("");
101277
101287
  setGradeBand("");
101278
101288
  setBloomLevelCode("");
101279
101289
  setSelectedQuestionType(questionTypeProp || "multiple_choice");
101290
+ setSelectedLanguage(language3);
101280
101291
  setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
101281
101292
  }
101282
- }, [isOpen, questionTypeProp]);
101293
+ }, [isOpen, questionTypeProp, language3]);
101294
+ const filteredCategories = React169.useMemo(() => {
101295
+ return categories;
101296
+ }, [categories]);
101283
101297
  const filteredTopics = React169.useMemo(() => {
101284
- if (!subjectCode) return [];
101285
- return topics.filter((t4) => t4.subjectCode === subjectCode);
101286
- }, [subjectCode, topics]);
101298
+ if (!subjectCode || !categoryCode) return [];
101299
+ return topics.filter(
101300
+ (t4) => t4.subjectCode === subjectCode
101301
+ /* && t.categoryCode === categoryCode */
101302
+ );
101303
+ }, [subjectCode, categoryCode, topics]);
101304
+ const handleSubjectChange = (newSubjectCode) => {
101305
+ setSubjectCode(newSubjectCode);
101306
+ setCategoryCode("");
101307
+ setTopicCode("");
101308
+ };
101309
+ const handleCategoryChange = (newCategoryCode) => {
101310
+ setCategoryCode(newCategoryCode);
101311
+ setTopicCode("");
101312
+ };
101287
101313
  const handleApiKeyModalClose = () => {
101288
101314
  setIsApiKeyManagerModalOpen(false);
101289
101315
  setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
101290
101316
  };
101291
101317
  const handleSubmit = async () => {
101292
- if (!prompt.trim()) {
101293
- setError("Please provide a prompt for the question.");
101318
+ if (!subjectCode || !categoryCode || !topicCode || !gradeBand || !bloomLevelCode) {
101319
+ setError("Please fill in all required metadata fields: Subject, Category, Topic, Grade Level, and Bloom's Level.");
101294
101320
  return;
101295
101321
  }
101296
101322
  const geminiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
@@ -101304,14 +101330,15 @@ var AIQuestionGeneratorModal = ({
101304
101330
  setIsLoading(true);
101305
101331
  try {
101306
101332
  const quizContext = {
101307
- plannedTopic: prompt,
101333
+ plannedTopic: additionalInfo.trim() || topicCode,
101308
101334
  originalSubject: subjectCode,
101309
101335
  originalTopic: topicCode,
101336
+ originalCategory: categoryCode,
101310
101337
  gradeBand,
101311
101338
  plannedBloomLevel: bloomLevelCode
101312
101339
  };
101313
101340
  const baseClientInput = {
101314
- language: language3,
101341
+ language: selectedLanguage,
101315
101342
  difficulty: "Medium",
101316
101343
  quizContext
101317
101344
  };
@@ -101324,33 +101351,32 @@ var AIQuestionGeneratorModal = ({
101324
101351
  generatedResult = await generateMCQQuestion({ ...baseClientInput, numberOfOptions }, geminiKey);
101325
101352
  break;
101326
101353
  case "multiple_response":
101327
- generatedResult = await generateMRQQuestion({ ...baseClientInput, numberOfOptions, minCorrectAnswers, maxCorrectAnswers }, geminiKey);
101354
+ generatedResult = await generateMRQQuestion({ ...baseClientInput, numberOfOptions: 5, minCorrectAnswers: 2, maxCorrectAnswers: 3 }, geminiKey);
101328
101355
  break;
101329
101356
  case "short_answer":
101330
- generatedResult = await generateShortAnswerQuestion({ ...baseClientInput, isCaseSensitive }, geminiKey);
101357
+ generatedResult = await generateShortAnswerQuestion({ ...baseClientInput, isCaseSensitive: false }, geminiKey);
101331
101358
  break;
101332
101359
  case "numeric":
101333
101360
  generatedResult = await generateNumericQuestion({ ...baseClientInput, allowDecimals: true, tolerance: 0 }, geminiKey);
101334
101361
  break;
101335
101362
  case "fill_in_the_blanks":
101336
- generatedResult = await generateFillInTheBlanksQuestion({ ...baseClientInput, numberOfBlanks, isCaseSensitive }, geminiKey);
101363
+ generatedResult = await generateFillInTheBlanksQuestion({ ...baseClientInput, numberOfBlanks: 2, isCaseSensitive: false }, geminiKey);
101337
101364
  break;
101338
101365
  case "sequence":
101339
- generatedResult = await generateSequenceQuestion({ ...baseClientInput, numberOfItems: numberOfSequenceItems }, geminiKey);
101366
+ generatedResult = await generateSequenceQuestion({ ...baseClientInput, numberOfItems: 4 }, geminiKey);
101340
101367
  break;
101341
101368
  case "matching":
101342
- generatedResult = await generateMatchingQuestion({ ...baseClientInput, numberOfPairs: numberOfMatchingPairs, shuffleOptions: true }, geminiKey);
101369
+ generatedResult = await generateMatchingQuestion({ ...baseClientInput, numberOfPairs: 4, shuffleOptions: true }, geminiKey);
101343
101370
  break;
101344
101371
  default:
101345
101372
  throw new Error(`AI generation for '${finalQuestionType}' is not implemented in this flow.`);
101346
101373
  }
101347
- if (generatedResult.error) {
101348
- throw new Error(generatedResult.error);
101349
- }
101374
+ if (generatedResult.error) throw new Error(generatedResult.error);
101350
101375
  if (generatedResult.question) {
101351
101376
  const completeQuestion = generatedResult.question;
101352
101377
  completeQuestion.subject = subjectCode;
101353
101378
  completeQuestion.topic = topicCode;
101379
+ completeQuestion.category = categoryCode;
101354
101380
  completeQuestion.gradeBand = gradeBand;
101355
101381
  completeQuestion.bloomLevel = bloomLevelCode;
101356
101382
  if (completeQuestion.points === void 0) completeQuestion.points = 10;
@@ -101376,47 +101402,13 @@ var AIQuestionGeneratorModal = ({
101376
101402
  const comboboxOptions = {
101377
101403
  subjects: subjects.map((s4) => ({ value: s4.code, label: s4.name })),
101378
101404
  topics: filteredTopics.map((t4) => ({ value: t4.code, label: t4.name })),
101405
+ categories: filteredCategories.map((c4) => ({ value: c4.code, label: c4.name })),
101379
101406
  gradeLevels: gradeLevels.map((g) => ({ value: g.code, label: g.name })),
101380
101407
  bloomLevels: bloomLevels.map((b2) => ({ value: b2.code, label: b2.name }))
101381
101408
  };
101382
- const renderSpecificParams = () => {
101383
- switch (finalQuestionType) {
101384
- case "multiple_choice":
101385
- case "multiple_response":
101386
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-num-options" }, "Number of Options (2-8)"), /* @__PURE__ */ React169__namespace.default.createElement(
101387
- Input,
101388
- {
101389
- id: "ai-num-options",
101390
- type: "number",
101391
- value: numberOfOptions,
101392
- onChange: (e3) => setNumberOfOptions(parseInt(e3.target.value, 10)),
101393
- min: 2,
101394
- max: 8
101395
- }
101396
- ), finalQuestionType === "multiple_response" && /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-min-correct" }, "Min Correct Answers"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-min-correct", type: "number", value: minCorrectAnswers, onChange: (e3) => setMinCorrectAnswers(parseInt(e3.target.value, 10)), min: 1, max: numberOfOptions }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-max-correct" }, "Max Correct Answers"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-max-correct", type: "number", value: maxCorrectAnswers, onChange: (e3) => setMaxCorrectAnswers(parseInt(e3.target.value, 10)), min: minCorrectAnswers, max: numberOfOptions })));
101397
- case "short_answer":
101398
- case "fill_in_the_blanks":
101399
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-2 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement("input", { type: "checkbox", id: "ai-case-sensitive", checked: isCaseSensitive, onChange: (e3) => setIsCaseSensitive(e3.target.checked), className: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary" }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-case-sensitive" }, "Case Sensitive Answers"), finalQuestionType === "fill_in_the_blanks" && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1 ml-4" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-num-blanks" }, "Number of Blanks (1-5)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-num-blanks", type: "number", value: numberOfBlanks, onChange: (e3) => setNumberOfBlanks(parseInt(e3.target.value, 10)), min: 1, max: 5 })));
101400
- case "sequence":
101401
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-num-seq-items" }, "Number of Items to Sequence (2-10)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-num-seq-items", type: "number", value: numberOfSequenceItems, onChange: (e3) => setNumberOfSequenceItems(parseInt(e3.target.value, 10)), min: 2, max: 10 }));
101402
- case "matching":
101403
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-num-match-pairs" }, "Number of Pairs to Match (2-8)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-num-match-pairs", type: "number", value: numberOfMatchingPairs, onChange: (e3) => setNumberOfMatchingPairs(parseInt(e3.target.value, 10)), min: 2, max: 8 }));
101404
- default:
101405
- return null;
101406
- }
101407
- };
101408
101409
  return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => {
101409
101410
  if (!open) onClose();
101410
- } }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-[600px]" }, /* @__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(WandSparkles, { className: "mr-2 h-6 w-6 text-primary" }), " AI Question Generator"), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, "Provide a prompt and metadata to generate a '", finalQuestionType, "' question.")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 py-4 max-h-[60vh] overflow-y-auto 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" }), !questionTypeProp && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-q-type-select" }, "Question Type"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedQuestionType, onValueChange: (v) => setSelectedQuestionType(v) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "ai-q-type-select" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, supportedQuestionTypesForAI.map((type) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: type.value, value: type.value }, type.label))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-prompt" }, "Prompt / Topic"), /* @__PURE__ */ React169__namespace.default.createElement(
101411
- Textarea,
101412
- {
101413
- id: "ai-prompt",
101414
- value: prompt,
101415
- onChange: (e3) => setPrompt(e3.target.value),
101416
- placeholder: "e.g., The process of photosynthesis, The causes of World War II, Basic Algebra Equations",
101417
- className: "min-h-[100px]"
101418
- }
101419
- )), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.subjects, value: subjectCode, onChange: setSubjectCode, placeholder: "Select or type a Subject..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Topic"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.topics, value: topicCode, onChange: setTopicCode, placeholder: "Select or type a Topic...", disabled: !subjectCode })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Grade Level"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.gradeLevels, value: gradeBand, onChange: setGradeBand, placeholder: "Select or type a Grade..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Bloom's Level"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.bloomLevels, value: bloomLevelCode, onChange: setBloomLevelCode, placeholder: "Select a Bloom's Level..." }))), renderSpecificParams(), 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)), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline" }, "Cancel")), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", onClick: handleSubmit, disabled: isLoading || !prompt.trim() || !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 Question")))), /* @__PURE__ */ React169__namespace.default.createElement(APIKeyManagerModal, { isOpen: isApiKeyManagerModalOpen, onClose: handleApiKeyModalClose }));
101411
+ } }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-[700px]" }, /* @__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(WandSparkles, { className: "mr-2 h-6 w-6 text-primary" }), " AI Question Generator"), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, "Provide metadata and optional info to generate a '", finalQuestionType, "' question.")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 py-4 max-h-[60vh] overflow-y-auto px-2" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-4" }, !questionTypeProp && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Question Type"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedQuestionType, onValueChange: (v) => setSelectedQuestionType(v) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, supportedQuestionTypesForAI.map((type) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: type.value, value: type.value }, type.label))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Language"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedLanguage, onValueChange: setSelectedLanguage }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, availableLanguages.map((lang) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: lang.value, value: lang.value }, lang.label)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-additional-info" }, "Additional Info (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "ai-additional-info", value: additionalInfo, onChange: (e3) => setAdditionalInfo(e3.target.value), placeholder: "Provide extra context, a specific topic, or a detailed prompt for the AI...", className: "min-h-[100px]" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Subject *"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.subjects, value: subjectCode, onChange: handleSubjectChange, placeholder: "Select Subject..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Category *"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.categories, value: categoryCode, onChange: handleCategoryChange, placeholder: "Select Category...", disabled: !subjectCode })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Topic *"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.topics, value: topicCode, onChange: setTopicCode, placeholder: "Select Topic...", disabled: !categoryCode })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Grade Level *"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.gradeLevels, value: gradeBand, onChange: setGradeBand, placeholder: "Select Grade..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Bloom's Level *"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.bloomLevels, value: bloomLevelCode, onChange: setBloomLevelCode, placeholder: "Select Bloom's Level..." }))), finalQuestionType === "multiple_choice" && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-num-options" }, "Number of Options (2-8)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-num-options", type: "number", value: numberOfOptions, onChange: (e3) => setNumberOfOptions(parseInt(e3.target.value, 10)), min: 2, max: 8 })), 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)), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline" }, "Cancel")), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", onClick: handleSubmit, disabled: isLoading || !geminiApiKeyExists || !subjectCode || !categoryCode || !topicCode || !gradeBand || !bloomLevelCode }, 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 Question")))), /* @__PURE__ */ React169__namespace.default.createElement(APIKeyManagerModal, { isOpen: isApiKeyManagerModalOpen, onClose: handleApiKeyModalClose }));
101420
101412
  };
101421
101413
 
101422
101414
  // src/react-ui/components/authoring/AIFullQuizGeneratorModal.tsx
@@ -101917,12 +101909,10 @@ init_react_shim();
101917
101909
  // src/services/TopicDataService.ts
101918
101910
  init_react_shim();
101919
101911
  var TopicDataService = class {
101920
- // ... saveData, mergeData, getData, clearData methods remain the same ...
101921
101912
  static saveData(data) {
101922
101913
  try {
101923
101914
  if (typeof window === "undefined") return;
101924
- const serializedData = JSON.stringify(data);
101925
- localStorage.setItem(this.STORAGE_KEY, serializedData);
101915
+ localStorage.setItem(this.STORAGE_KEY, JSON.stringify(data));
101926
101916
  } catch (error) {
101927
101917
  console.error("Error saving learning objectives to Local Storage:", error);
101928
101918
  }
@@ -101933,8 +101923,7 @@ var TopicDataService = class {
101933
101923
  newData.forEach((newLo) => {
101934
101924
  loMap.set(newLo.code, newLo);
101935
101925
  });
101936
- const mergedData = Array.from(loMap.values());
101937
- this.saveData(mergedData);
101926
+ this.saveData(Array.from(loMap.values()));
101938
101927
  }
101939
101928
  static getData() {
101940
101929
  try {
@@ -101949,7 +101938,7 @@ var TopicDataService = class {
101949
101938
  }
101950
101939
  static clearData() {
101951
101940
  try {
101952
- if (typeof window === "undefined") return;
101941
+ if (typeof window !== "undefined") return;
101953
101942
  localStorage.removeItem(this.STORAGE_KEY);
101954
101943
  } catch (error) {
101955
101944
  console.error("Error clearing learning objectives from Local Storage:", error);
@@ -101963,6 +101952,12 @@ var TopicDataService = class {
101963
101952
  const headerLine = lines.shift();
101964
101953
  const headers = headerLine.split(" ").map((h3) => h3.trim());
101965
101954
  const headerMap = headers.map((h3) => this.HEADER_MAP[h3] || null);
101955
+ const requiredHeaders = ["LO ID", "LO Name", "Subject", "Subject Code", "Category", "Category Code", "Topic", "Topic Code", "Grade", "Grade Code"];
101956
+ const missingHeaders = requiredHeaders.filter((h3) => !headers.includes(h3));
101957
+ if (missingHeaders.length > 0) {
101958
+ const errorMsg = `Invalid TSV header. Missing required columns: ${missingHeaders.join(", ")}`;
101959
+ return { data: [], errors: [errorMsg] };
101960
+ }
101966
101961
  const data = [];
101967
101962
  const errors2 = [];
101968
101963
  lines.forEach((line, index3) => {
@@ -101971,29 +101966,30 @@ var TopicDataService = class {
101971
101966
  headerMap.forEach((propName, i2) => {
101972
101967
  if (propName) {
101973
101968
  const value = values[i2]?.trim() || "";
101974
- if (propName === "keywords" || propName === "stemElements" || propName === "bloomLevelsGuideline") {
101969
+ if (["keywords", "stemElements", "bloomLevelsGuideline"].includes(propName)) {
101975
101970
  rowObject[propName] = value.split(",").map((s4) => s4.trim()).filter(Boolean);
101976
101971
  } else {
101977
101972
  rowObject[propName] = value;
101978
101973
  }
101979
101974
  }
101980
101975
  });
101981
- if (!rowObject.code) {
101982
- errors2.push(`Line ${index3 + 2}: Missing required value for 'LO ID'.`);
101976
+ if (!rowObject.code || !rowObject.name) {
101977
+ errors2.push(`Line ${index3 + 2}: Missing required values for 'LO ID' or 'LO Name'.`);
101983
101978
  return;
101984
101979
  }
101985
- if (!rowObject.name) {
101986
- rowObject.name = rowObject.code;
101987
- }
101988
101980
  const learningObjective = {
101989
101981
  id: generateUniqueId("lo_"),
101990
101982
  code: rowObject.code,
101991
101983
  name: rowObject.name,
101992
101984
  description: rowObject.description,
101993
101985
  subject: rowObject.subject || "",
101986
+ subjectCode: rowObject.subjectCode,
101994
101987
  category: rowObject.category || "",
101988
+ categoryCode: rowObject.categoryCode,
101995
101989
  topic: rowObject.topic || "",
101990
+ topicCode: rowObject.topicCode,
101996
101991
  grade: rowObject.grade || "",
101992
+ gradeCode: rowObject.gradeCode,
101997
101993
  keywords: rowObject.keywords || [],
101998
101994
  stemElements: rowObject.stemElements || [],
101999
101995
  bloomLevelsGuideline: rowObject.bloomLevelsGuideline || []
@@ -102024,17 +102020,23 @@ var TopicDataService = class {
102024
102020
  }
102025
102021
  };
102026
102022
  TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
102027
- // Define a map for flexible header mapping
102028
102023
  TopicDataService.HEADER_MAP = {
102029
102024
  "LO ID": "code",
102030
102025
  "LO Name": "name",
102031
- // Ready for future addition
102032
102026
  "LO Description": "description",
102033
102027
  "Subject": "subject",
102028
+ "Subject Code": "subjectCode",
102029
+ // New
102034
102030
  "Category": "category",
102031
+ "Category Code": "categoryCode",
102032
+ // New
102035
102033
  "Topic": "topic",
102034
+ "Topic Code": "topicCode",
102035
+ // New
102036
102036
  "Keywords": "keywords",
102037
102037
  "Grade": "grade",
102038
+ "Grade Code": "gradeCode",
102039
+ // New
102038
102040
  "STEM Element(s)": "stemElements",
102039
102041
  "Bloom\u2019s Level(s) Guideline": "bloomLevelsGuideline"
102040
102042
  };
@@ -5,7 +5,7 @@ import { Q as QuizResultType, U as UserAnswerType, n as PracticeSession, o as Pr
5
5
  export { j as AchievementDefinition, r as ActivityCalendarData, u as AnalysisReport, A as AnswerDetail, x as ChatContext, C as ChatMessage, v as DashboardCardConfig, D as DashboardCardId, w as DashboardLayout, y as Goal, G as GoalType, t as ImageContextItem, I as ImportError, K as KnowledgeCard, L as LearningAnalysis, e as PerformanceByBloomLevel, b as PerformanceByCategory, d as PerformanceByDifficulty, P as PerformanceByLearningObjective, c as PerformanceByTopic, f as PerformanceMetric, s as PerformanceSummary, k as PracticeDifficulty, q as PracticeTopicSummary, g as QuestionReview, R as RoadmapItem, T as TestCaseResult, a as UserAnswers, W as WeeklyRoadmap } from './ai-ecosystem-DyQYZbyX.cjs';
6
6
  import * as React$1 from 'react';
7
7
  import React__default, { ReactNode } from 'react';
8
- export { c as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, e as APIKeyManagerModal, f as ApiKeySettings, m as ApproachManager, B as BloomLevelManager, C as CategoryManager, l as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, n as MetadataImportControls, M as MetadataTabs, h as QuestionFilters, i as QuestionFormDialog, g as QuestionList, a as QuestionPreviewModal, k as QuestionTypeManager, Q as QuizAuthoringTool, b as QuizSettingsForm, d as SCORMExportModal, S as SelectedQuestionsPanel, j as SubjectManager, o as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-6AR8w2TO.cjs';
8
+ export { c as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, e as APIKeyManagerModal, f as ApiKeySettings, m as ApproachManager, B as BloomLevelManager, C as CategoryManager, l as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, n as MetadataImportControls, M as MetadataTabs, h as QuestionFilters, i as QuestionFormDialog, g as QuestionList, a as QuestionPreviewModal, k as QuestionTypeManager, Q as QuizAuthoringTool, b as QuizSettingsForm, d as SCORMExportModal, S as SelectedQuestionsPanel, j as SubjectManager, o as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-Bvhmyef9.cjs';
9
9
  import * as class_variance_authority_types from 'class-variance-authority/types';
10
10
  import { VariantProps } from 'class-variance-authority';
11
11
  import * as LabelPrimitive from '@radix-ui/react-label';
@@ -5,7 +5,7 @@ import { Q as QuizResultType, U as UserAnswerType, n as PracticeSession, o as Pr
5
5
  export { j as AchievementDefinition, r as ActivityCalendarData, u as AnalysisReport, A as AnswerDetail, x as ChatContext, C as ChatMessage, v as DashboardCardConfig, D as DashboardCardId, w as DashboardLayout, y as Goal, G as GoalType, t as ImageContextItem, I as ImportError, K as KnowledgeCard, L as LearningAnalysis, e as PerformanceByBloomLevel, b as PerformanceByCategory, d as PerformanceByDifficulty, P as PerformanceByLearningObjective, c as PerformanceByTopic, f as PerformanceMetric, s as PerformanceSummary, k as PracticeDifficulty, q as PracticeTopicSummary, g as QuestionReview, R as RoadmapItem, T as TestCaseResult, a as UserAnswers, W as WeeklyRoadmap } from './ai-ecosystem-Qa_SdE2T.js';
6
6
  import * as React$1 from 'react';
7
7
  import React__default, { ReactNode } from 'react';
8
- export { c as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, e as APIKeyManagerModal, f as ApiKeySettings, m as ApproachManager, B as BloomLevelManager, C as CategoryManager, l as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, n as MetadataImportControls, M as MetadataTabs, h as QuestionFilters, i as QuestionFormDialog, g as QuestionList, a as QuestionPreviewModal, k as QuestionTypeManager, Q as QuizAuthoringTool, b as QuizSettingsForm, d as SCORMExportModal, S as SelectedQuestionsPanel, j as SubjectManager, o as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-DAXYZdrz.js';
8
+ export { c as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, e as APIKeyManagerModal, f as ApiKeySettings, m as ApproachManager, B as BloomLevelManager, C as CategoryManager, l as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, n as MetadataImportControls, M as MetadataTabs, h as QuestionFilters, i as QuestionFormDialog, g as QuestionList, a as QuestionPreviewModal, k as QuestionTypeManager, Q as QuizAuthoringTool, b as QuizSettingsForm, d as SCORMExportModal, S as SelectedQuestionsPanel, j as SubjectManager, o as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-B2MPZYhi.js';
9
9
  import * as class_variance_authority_types from 'class-variance-authority/types';
10
10
  import { VariantProps } from 'class-variance-authority';
11
11
  import * as LabelPrimitive from '@radix-ui/react-label';