@thanh01.pmt/interactive-quiz-kit 1.0.65 → 1.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react-ui.mjs CHANGED
@@ -9019,7 +9019,7 @@ var translation_default = {
9019
9019
  subject: "Subject",
9020
9020
  category: "Category",
9021
9021
  topic: "Topic",
9022
- loId: "LO ID"
9022
+ code: "LO ID"
9023
9023
  },
9024
9024
  confirmModal: {
9025
9025
  title: "Confirm Data Import",
@@ -9434,7 +9434,7 @@ var translation_default2 = {
9434
9434
  subject: "M\xF4n h\u1ECDc",
9435
9435
  category: "Danh m\u1EE5c",
9436
9436
  topic: "Ch\u1EE7 \u0111\u1EC1",
9437
- loId: "M\xE3 MTH"
9437
+ code: "M\xE3 MTH"
9438
9438
  },
9439
9439
  confirmModal: {
9440
9440
  title: "X\xE1c nh\u1EADn Nh\u1EADp D\u1EEF li\u1EC7u",
@@ -37506,7 +37506,7 @@ var cva = (base3, config3) => (props) => {
37506
37506
 
37507
37507
  // src/react-ui/components/elements/label.tsx
37508
37508
  var labelVariants = cva(
37509
- "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
37509
+ "text-sm font-Medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
37510
37510
  );
37511
37511
  var Label2 = React169.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React169.createElement(
37512
37512
  Root3,
@@ -62744,7 +62744,7 @@ var MarkdownRenderer = ({
62744
62744
  },
62745
62745
  h1: ({ node: node2, ...props }) => /* @__PURE__ */ React169__default.createElement("h1", { ...props, className: "text-3xl font-bold mb-6 mt-8 first:mt-0" }),
62746
62746
  h2: ({ node: node2, ...props }) => /* @__PURE__ */ React169__default.createElement("h2", { ...props, className: "text-2xl font-semibold mb-4 mt-6" }),
62747
- h3: ({ node: node2, ...props }) => /* @__PURE__ */ React169__default.createElement("h3", { ...props, className: "text-xl font-medium mb-3 mt-5" }),
62747
+ h3: ({ node: node2, ...props }) => /* @__PURE__ */ React169__default.createElement("h3", { ...props, className: "text-xl font-Medium mb-3 mt-5" }),
62748
62748
  ul: ({ node: node2, ...props }) => /* @__PURE__ */ React169__default.createElement("ul", { ...props, className: "my-4 space-y-2 list-disc list-inside" }),
62749
62749
  ol: ({ node: node2, ...props }) => /* @__PURE__ */ React169__default.createElement("ol", { ...props, className: "my-4 space-y-2 list-decimal list-inside" }),
62750
62750
  p: ({ node: node2, ...props }) => /* @__PURE__ */ React169__default.createElement("p", { ...props, className: "mb-4 leading-7" }),
@@ -63218,7 +63218,7 @@ var Input = React169.forwardRef(
63218
63218
  {
63219
63219
  type,
63220
63220
  className: cn(
63221
- "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
63221
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-Medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
63222
63222
  className
63223
63223
  ),
63224
63224
  ref,
@@ -63393,7 +63393,7 @@ init_react_shim();
63393
63393
  // src/react-ui/components/elements/button.tsx
63394
63394
  init_react_shim();
63395
63395
  var buttonVariants = cva(
63396
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
63396
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-Medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
63397
63397
  {
63398
63398
  variants: {
63399
63399
  variant: {
@@ -68346,7 +68346,7 @@ var MatchingQuestionUI = ({
68346
68346
  if (showCorrectAnswer && selectedOptionId) {
68347
68347
  borderColor = isSelectionCorrect ? "border-green-500" : "border-destructive";
68348
68348
  }
68349
- return /* @__PURE__ */ React169__default.createElement("div", { key: promptItem.id, className: `p-3 border rounded-md ${borderColor} transition-colors bg-background` }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `select-prompt-${promptItem.id}`, className: "font-medium text-base block mb-2 whitespace-normal" }, /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: promptItem.content })), /* @__PURE__ */ React169__default.createElement(
68349
+ return /* @__PURE__ */ React169__default.createElement("div", { key: promptItem.id, className: `p-3 border rounded-md ${borderColor} transition-colors bg-background` }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `select-prompt-${promptItem.id}`, className: "font-Medium text-base block mb-2 whitespace-normal" }, /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: promptItem.content })), /* @__PURE__ */ React169__default.createElement(
68350
68350
  Select2,
68351
68351
  {
68352
68352
  value: selectedOptionId,
@@ -68401,7 +68401,7 @@ var DragAndDropQuestionUI = ({
68401
68401
  } else {
68402
68402
  itemStyle += " border-muted";
68403
68403
  }
68404
- return /* @__PURE__ */ React169__default.createElement("div", { key: item.id, className: itemStyle }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `select-draggable-${item.id}`, className: "font-medium text-base mb-2 sm:mb-0 sm:mr-4 flex-1" }, /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: item.content })), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-2 w-full sm:w-auto" }, /* @__PURE__ */ React169__default.createElement(
68404
+ return /* @__PURE__ */ React169__default.createElement("div", { key: item.id, className: itemStyle }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `select-draggable-${item.id}`, className: "font-Medium text-base mb-2 sm:mb-0 sm:mr-4 flex-1" }, /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: item.content })), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-2 w-full sm:w-auto" }, /* @__PURE__ */ React169__default.createElement(
68405
68405
  Select2,
68406
68406
  {
68407
68407
  value: selectedDropZoneId,
@@ -95417,7 +95417,7 @@ var TabsTrigger2 = React169.forwardRef(({ className, ...props }, ref) => /* @__P
95417
95417
  {
95418
95418
  ref,
95419
95419
  className: cn(
95420
- "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
95420
+ "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-Medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
95421
95421
  className
95422
95422
  ),
95423
95423
  ...props
@@ -96117,7 +96117,7 @@ var AccordionTrigger2 = React169.forwardRef(({ className, children, ...props },
96117
96117
  {
96118
96118
  ref,
96119
96119
  className: cn(
96120
- "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
96120
+ "flex flex-1 items-center justify-between py-4 font-Medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
96121
96121
  className
96122
96122
  ),
96123
96123
  ...props
@@ -96904,7 +96904,7 @@ var QuizResult = ({
96904
96904
  }
96905
96905
  return String(answer);
96906
96906
  };
96907
- return /* @__PURE__ */ React169__default.createElement(Card, { className: "w-full max-w-3xl mx-auto shadow-xl" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-3xl font-headline text-center" }, t4("practiceFlow.results.title", { quizTitle })), /* @__PURE__ */ React169__default.createElement(CardDescription, { className: "text-center text-lg" }, t4("practiceFlow.results.description"))), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React169__default.createElement(Card, { className: "bg-secondary/50" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-xl flex items-center" }, /* @__PURE__ */ React169__default.createElement(BarChart2, { className: "mr-2 h-5 w-5 text-primary" }), t4("practiceFlow.results.overallScore"))), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "grid grid-cols-1 md:grid-cols-3 gap-4 text-center" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "text-3xl font-bold text-primary" }, result.score, " / ", result.maxScore), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("practiceFlow.results.points"))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "text-3xl font-bold text-primary" }, result.percentage.toFixed(2), "%"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("practiceFlow.results.percentage"))), /* @__PURE__ */ React169__default.createElement("div", null, result.passed !== void 0 && (result.passed ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col items-center text-green-600" }, /* @__PURE__ */ React169__default.createElement(CircleCheckBig, { className: "h-10 w-10" }), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xl font-semibold mt-1" }, t4("practiceFlow.results.passed"))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col items-center text-destructive" }, /* @__PURE__ */ React169__default.createElement(CircleX, { className: "h-10 w-10" }), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xl font-semibold mt-1" }, t4("practiceFlow.results.failed"))))))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 text-sm" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md" }, /* @__PURE__ */ React169__default.createElement(Clock, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ React169__default.createElement("span", null, t4("practiceFlow.results.timeSpent")), /* @__PURE__ */ React169__default.createElement("span", { className: "font-semibold" }, result.totalTimeSpentSeconds?.toFixed(0) ?? "N/A", " ", t4("practiceFlow.results.timeUnit"))), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md" }, /* @__PURE__ */ React169__default.createElement(Percent, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ React169__default.createElement("span", null, t4("practiceFlow.results.avgTimePerQuestion")), /* @__PURE__ */ React169__default.createElement("span", { className: "font-semibold" }, result.averageTimePerQuestionSeconds?.toFixed(1) ?? "N/A", " ", t4("practiceFlow.results.timeUnit")))), result.scormStatus && result.scormStatus !== "idle" && result.scormStatus !== "no_api" && /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-lg" }, "SCORM Sync Status")), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement("p", { className: `flex items-center ${result.scormStatus === "error" ? "text-destructive" : "text-muted-foreground"}` }, result.scormStatus === "error" && /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "mr-2 h-4 w-4" }), "Status: ", /* @__PURE__ */ React169__default.createElement("span", { className: "font-semibold ml-1" }, result.scormStatus)), result.scormError && /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-destructive mt-1" }, "Details: ", result.scormError))), result.webhookStatus && result.webhookStatus !== "idle" && /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-lg" }, "Webhook Sync Status")), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement("p", { className: `flex items-center ${result.webhookStatus === "error" ? "text-destructive" : "text-muted-foreground"}` }, result.webhookStatus === "error" && /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "mr-2 h-4 w-4" }), "Status: ", /* @__PURE__ */ React169__default.createElement("span", { className: "font-semibold ml-1" }, result.webhookStatus)), result.webhookError && /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-destructive mt-1" }, "Details: ", result.webhookError))), /* @__PURE__ */ React169__default.createElement(Accordion2, { type: "single", collapsible: true, className: "w-full" }, /* @__PURE__ */ React169__default.createElement(AccordionItem2, { value: "question-breakdown" }, /* @__PURE__ */ React169__default.createElement(AccordionTrigger2, { className: "text-lg font-semibold" }, t4("practiceFlow.results.questionBreakdown")), /* @__PURE__ */ React169__default.createElement(AccordionContent2, null, /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-[300px] pr-4" }, /* @__PURE__ */ React169__default.createElement("ul", { className: "space-y-4" }, result.questionResults.map((qResult, index3) => /* @__PURE__ */ React169__default.createElement("li", { key: qResult.questionId, className: "p-4 border rounded-md bg-background" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold" }, t4("common.questions", { count: index3 + 1 })), qResult.isCorrect ? /* @__PURE__ */ React169__default.createElement("span", { className: "text-green-600 font-medium flex items-center" }, /* @__PURE__ */ React169__default.createElement(CircleCheckBig, { className: "mr-1 h-4 w-4" }), " ", t4("practiceFlow.results.passed")) : /* @__PURE__ */ React169__default.createElement("span", { className: "text-destructive font-medium flex items-center" }, /* @__PURE__ */ React169__default.createElement(CircleX, { className: "mr-1 h-4 w-4" }), " ", t4("practiceFlow.results.failed"))), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React169__default.createElement("span", { className: "font-medium" }, t4("practiceFlow.results.yourAnswer")), " ", getAnswerDisplay(qResult.userAnswer)), !qResult.isCorrect && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React169__default.createElement("span", { className: "font-medium" }, t4("practiceFlow.results.correctAnswer")), " ", getAnswerDisplay(qResult.correctAnswer)), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, /* @__PURE__ */ React169__default.createElement("span", { className: "font-medium" }, t4("practiceFlow.results.pointsEarned")), " ", qResult.pointsEarned), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, /* @__PURE__ */ React169__default.createElement("span", { className: "font-medium" }, t4("practiceFlow.results.timeSpent")), " ", qResult.timeSpentSeconds?.toFixed(0) ?? "N/A", t4("practiceFlow.results.timeUnit")))))))))), /* @__PURE__ */ React169__default.createElement(CardFooter, { className: "flex flex-col sm:flex-row justify-between gap-2" }, onExitQuiz && /* @__PURE__ */ React169__default.createElement(Button, { variant: "outline", onClick: onExitQuiz, className: "w-full sm:w-auto" }, /* @__PURE__ */ React169__default.createElement(LogOut, { className: "mr-2 h-4 w-4" }), t4("common.exit")), showReviewButton && onGenerateReview && /* @__PURE__ */ React169__default.createElement(
96907
+ return /* @__PURE__ */ React169__default.createElement(Card, { className: "w-full max-w-3xl mx-auto shadow-xl" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-3xl font-headline text-center" }, t4("practiceFlow.results.title", { quizTitle })), /* @__PURE__ */ React169__default.createElement(CardDescription, { className: "text-center text-lg" }, t4("practiceFlow.results.description"))), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React169__default.createElement(Card, { className: "bg-secondary/50" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-xl flex items-center" }, /* @__PURE__ */ React169__default.createElement(BarChart2, { className: "mr-2 h-5 w-5 text-primary" }), t4("practiceFlow.results.overallScore"))), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "grid grid-cols-1 md:grid-cols-3 gap-4 text-center" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "text-3xl font-bold text-primary" }, result.score, " / ", result.maxScore), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("practiceFlow.results.points"))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "text-3xl font-bold text-primary" }, result.percentage.toFixed(2), "%"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("practiceFlow.results.percentage"))), /* @__PURE__ */ React169__default.createElement("div", null, result.passed !== void 0 && (result.passed ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col items-center text-green-600" }, /* @__PURE__ */ React169__default.createElement(CircleCheckBig, { className: "h-10 w-10" }), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xl font-semibold mt-1" }, t4("practiceFlow.results.passed"))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col items-center text-destructive" }, /* @__PURE__ */ React169__default.createElement(CircleX, { className: "h-10 w-10" }), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xl font-semibold mt-1" }, t4("practiceFlow.results.failed"))))))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 text-sm" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md" }, /* @__PURE__ */ React169__default.createElement(Clock, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ React169__default.createElement("span", null, t4("practiceFlow.results.timeSpent")), /* @__PURE__ */ React169__default.createElement("span", { className: "font-semibold" }, result.totalTimeSpentSeconds?.toFixed(0) ?? "N/A", " ", t4("practiceFlow.results.timeUnit"))), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md" }, /* @__PURE__ */ React169__default.createElement(Percent, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ React169__default.createElement("span", null, t4("practiceFlow.results.avgTimePerQuestion")), /* @__PURE__ */ React169__default.createElement("span", { className: "font-semibold" }, result.averageTimePerQuestionSeconds?.toFixed(1) ?? "N/A", " ", t4("practiceFlow.results.timeUnit")))), result.scormStatus && result.scormStatus !== "idle" && result.scormStatus !== "no_api" && /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-lg" }, "SCORM Sync Status")), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement("p", { className: `flex items-center ${result.scormStatus === "error" ? "text-destructive" : "text-muted-foreground"}` }, result.scormStatus === "error" && /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "mr-2 h-4 w-4" }), "Status: ", /* @__PURE__ */ React169__default.createElement("span", { className: "font-semibold ml-1" }, result.scormStatus)), result.scormError && /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-destructive mt-1" }, "Details: ", result.scormError))), result.webhookStatus && result.webhookStatus !== "idle" && /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-lg" }, "Webhook Sync Status")), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement("p", { className: `flex items-center ${result.webhookStatus === "error" ? "text-destructive" : "text-muted-foreground"}` }, result.webhookStatus === "error" && /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "mr-2 h-4 w-4" }), "Status: ", /* @__PURE__ */ React169__default.createElement("span", { className: "font-semibold ml-1" }, result.webhookStatus)), result.webhookError && /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-destructive mt-1" }, "Details: ", result.webhookError))), /* @__PURE__ */ React169__default.createElement(Accordion2, { type: "single", collapsible: true, className: "w-full" }, /* @__PURE__ */ React169__default.createElement(AccordionItem2, { value: "question-breakdown" }, /* @__PURE__ */ React169__default.createElement(AccordionTrigger2, { className: "text-lg font-semibold" }, t4("practiceFlow.results.questionBreakdown")), /* @__PURE__ */ React169__default.createElement(AccordionContent2, null, /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-[300px] pr-4" }, /* @__PURE__ */ React169__default.createElement("ul", { className: "space-y-4" }, result.questionResults.map((qResult, index3) => /* @__PURE__ */ React169__default.createElement("li", { key: qResult.questionId, className: "p-4 border rounded-md bg-background" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold" }, t4("common.questions", { count: index3 + 1 })), qResult.isCorrect ? /* @__PURE__ */ React169__default.createElement("span", { className: "text-green-600 font-Medium flex items-center" }, /* @__PURE__ */ React169__default.createElement(CircleCheckBig, { className: "mr-1 h-4 w-4" }), " ", t4("practiceFlow.results.passed")) : /* @__PURE__ */ React169__default.createElement("span", { className: "text-destructive font-Medium flex items-center" }, /* @__PURE__ */ React169__default.createElement(CircleX, { className: "mr-1 h-4 w-4" }), " ", t4("practiceFlow.results.failed"))), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React169__default.createElement("span", { className: "font-Medium" }, t4("practiceFlow.results.yourAnswer")), " ", getAnswerDisplay(qResult.userAnswer)), !qResult.isCorrect && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React169__default.createElement("span", { className: "font-Medium" }, t4("practiceFlow.results.correctAnswer")), " ", getAnswerDisplay(qResult.correctAnswer)), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, /* @__PURE__ */ React169__default.createElement("span", { className: "font-Medium" }, t4("practiceFlow.results.pointsEarned")), " ", qResult.pointsEarned), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, /* @__PURE__ */ React169__default.createElement("span", { className: "font-Medium" }, t4("practiceFlow.results.timeSpent")), " ", qResult.timeSpentSeconds?.toFixed(0) ?? "N/A", t4("practiceFlow.results.timeUnit")))))))))), /* @__PURE__ */ React169__default.createElement(CardFooter, { className: "flex flex-col sm:flex-row justify-between gap-2" }, onExitQuiz && /* @__PURE__ */ React169__default.createElement(Button, { variant: "outline", onClick: onExitQuiz, className: "w-full sm:w-auto" }, /* @__PURE__ */ React169__default.createElement(LogOut, { className: "mr-2 h-4 w-4" }), t4("common.exit")), showReviewButton && onGenerateReview && /* @__PURE__ */ React169__default.createElement(
96908
96908
  Button,
96909
96909
  {
96910
96910
  onClick: onGenerateReview,
@@ -97219,7 +97219,7 @@ var QuizDataManagement = ({ onQuizLoad, currentQuiz }) => {
97219
97219
  });
97220
97220
  }
97221
97221
  };
97222
- return /* @__PURE__ */ React169__default.createElement(Card, { className: "w-full shadow-lg" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, null, "Quiz Data Management"), /* @__PURE__ */ React169__default.createElement(CardDescription, null, "Import a quiz from a JSON file or export the current quiz configuration.")), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col gap-2 mb-4" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "quiz-file-input", className: "text-sm font-medium" }, "Import Quiz (JSON)"), /* @__PURE__ */ React169__default.createElement("div", { className: "relative" }, /* @__PURE__ */ React169__default.createElement(
97222
+ return /* @__PURE__ */ React169__default.createElement(Card, { className: "w-full shadow-lg" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, null, "Quiz Data Management"), /* @__PURE__ */ React169__default.createElement(CardDescription, null, "Import a quiz from a JSON file or export the current quiz configuration.")), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col gap-2 mb-4" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "quiz-file-input", className: "text-sm font-Medium" }, "Import Quiz (JSON)"), /* @__PURE__ */ React169__default.createElement("div", { className: "relative" }, /* @__PURE__ */ React169__default.createElement(
97223
97223
  Input,
97224
97224
  {
97225
97225
  id: "quiz-file-input",
@@ -97227,7 +97227,7 @@ var QuizDataManagement = ({ onQuizLoad, currentQuiz }) => {
97227
97227
  accept: ".json",
97228
97228
  ref: fileInputRef,
97229
97229
  onChange: handleFileChange,
97230
- className: "w-full h-10 px-3 py-2 border border-input bg-background text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
97230
+ className: "w-full h-10 px-3 py-2 border border-input bg-background text-sm file:border-0 file:bg-transparent file:text-sm file:font-Medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
97231
97231
  }
97232
97232
  ))), error && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-destructive flex items-center" }, /* @__PURE__ */ React169__default.createElement(CircleAlert, { className: "mr-1 h-4 w-4" }), " ", error)), /* @__PURE__ */ React169__default.createElement(CardFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleExportQuiz, disabled: !currentQuiz, variant: "outline" }, /* @__PURE__ */ React169__default.createElement(Download, { className: "mr-2 h-4 w-4" }), " Export Current Quiz")));
97233
97233
  };
@@ -97662,6 +97662,7 @@ var Close = DialogClose;
97662
97662
 
97663
97663
  // src/react-ui/components/elements/dialog.tsx
97664
97664
  var Dialog2 = Root10;
97665
+ var DialogTrigger2 = Trigger4;
97665
97666
  var DialogPortal2 = Portal3;
97666
97667
  var DialogClose2 = Close;
97667
97668
  var DialogOverlay2 = React169.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React169.createElement(
@@ -97860,7 +97861,7 @@ var CommandGroup = React169.forwardRef(({ className, ...props }, ref) => /* @__P
97860
97861
  {
97861
97862
  ref,
97862
97863
  className: cn(
97863
- "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
97864
+ "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-Medium [&_[cmdk-group-heading]]:text-muted-foreground",
97864
97865
  className
97865
97866
  ),
97866
97867
  ...props
@@ -98261,7 +98262,7 @@ var TrueFalseQuestionForm = ({ question: question2, onFormChange }) => {
98261
98262
  const handleCorrectAnswerChange = (value) => {
98262
98263
  onFormChange({ correctAnswer: value === "true" });
98263
98264
  };
98264
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "True/False Specifics"), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Correct Answer"), /* @__PURE__ */ React169__default.createElement(
98265
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "True/False Specifics"), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Correct Answer"), /* @__PURE__ */ React169__default.createElement(
98265
98266
  RadioGroup2,
98266
98267
  {
98267
98268
  value: question2.correctAnswer ? "true" : "false",
@@ -98298,7 +98299,7 @@ var MultipleChoiceQuestionForm = ({ question: question2, onFormChange }) => {
98298
98299
  const handleCorrectAnswerChange = (optionId) => {
98299
98300
  onFormChange({ correctAnswerId: optionId });
98300
98301
  };
98301
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Multiple Choice Specifics"), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Options"), question2.options.length === 0 && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No options added yet."), /* @__PURE__ */ React169__default.createElement(
98302
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Multiple Choice Specifics"), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Options"), question2.options.length === 0 && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No options added yet."), /* @__PURE__ */ React169__default.createElement(
98302
98303
  RadioGroup2,
98303
98304
  {
98304
98305
  value: question2.correctAnswerId,
@@ -98360,7 +98361,7 @@ var MultipleResponseQuestionForm = ({ question: question2, onFormChange }) => {
98360
98361
  const newCorrectAnswerIds = question2.correctAnswerIds.filter((id3) => id3 !== optionIdToDelete);
98361
98362
  onFormChange({ options: newOptions, correctAnswerIds: newCorrectAnswerIds });
98362
98363
  };
98363
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Multiple Response Specifics"), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Options (Select all correct answers)"), question2.options.length === 0 && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No options added yet."), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3 mt-2" }, question2.options.map((option, index3) => (
98364
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Multiple Response Specifics"), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Options (Select all correct answers)"), question2.options.length === 0 && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No options added yet."), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3 mt-2" }, question2.options.map((option, index3) => (
98364
98365
  // *** CHANGED: Adjusted layout for SimpleMarkdownEditor ***
98365
98366
  /* @__PURE__ */ React169__default.createElement("div", { key: option.id, className: "flex items-start space-x-2 p-2 border rounded-md bg-background hover:shadow-sm" }, /* @__PURE__ */ React169__default.createElement(
98366
98367
  Checkbox2,
@@ -98582,7 +98583,7 @@ var ShortAnswerQuestionForm = ({ question: question2, onFormChange }) => {
98582
98583
  const toggleCaseSensitive = (checked) => {
98583
98584
  onFormChange({ isCaseSensitive: checked });
98584
98585
  };
98585
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Short Answer Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Accepted Answers"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, "Enter all valid plain text answers. The system will check against these."), question2.acceptedAnswers.length === 0 && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No accepted answers defined yet."), question2.acceptedAnswers.map((answer, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: index3, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(
98586
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Short Answer Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Accepted Answers"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, "Enter all valid plain text answers. The system will check against these."), question2.acceptedAnswers.length === 0 && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No accepted answers defined yet."), question2.acceptedAnswers.map((answer, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: index3, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(
98586
98587
  Input,
98587
98588
  {
98588
98589
  type: "text",
@@ -98632,7 +98633,7 @@ var NumericQuestionForm = ({ question: question2, onFormChange }) => {
98632
98633
  onFormChange({ tolerance: numValue });
98633
98634
  }
98634
98635
  };
98635
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Numeric Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `num-answer-${question2.id}` }, "Correct Numerical Answer"), /* @__PURE__ */ React169__default.createElement(
98636
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Numeric Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `num-answer-${question2.id}` }, "Correct Numerical Answer"), /* @__PURE__ */ React169__default.createElement(
98636
98637
  Input,
98637
98638
  {
98638
98639
  id: `num-answer-${question2.id}`,
@@ -98745,7 +98746,7 @@ var FillInTheBlanksQuestionForm = ({ question: question2, onFormChange }) => {
98745
98746
  const toggleCaseSensitive = (checked) => {
98746
98747
  onFormChange({ isCaseSensitive: checked });
98747
98748
  };
98748
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Fill In The Blanks Specifics"), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Question Segments"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, "Construct the question by adding text and blank segments. The prompt in the main editor will be shown above these segments."), question2.segments.map((segment, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: `segment-${index3}`, className: "flex items-start space-x-2 mt-2 p-2 border rounded-md bg-background" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex-grow space-y-1" }, /* @__PURE__ */ React169__default.createElement("span", { className: "text-xs font-medium capitalize text-muted-foreground" }, segment.type, " Segment ", index3 + 1), segment.type === "text" ? (
98749
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Fill In The Blanks Specifics"), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Question Segments"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, "Construct the question by adding text and blank segments. The prompt in the main editor will be shown above these segments."), question2.segments.map((segment, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: `segment-${index3}`, className: "flex items-start space-x-2 mt-2 p-2 border rounded-md bg-background" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex-grow space-y-1" }, /* @__PURE__ */ React169__default.createElement("span", { className: "text-xs font-Medium capitalize text-muted-foreground" }, segment.type, " Segment ", index3 + 1), segment.type === "text" ? (
98749
98750
  // *** CHANGED: Replaced Textarea with SimpleMarkdownEditor ***
98750
98751
  /* @__PURE__ */ React169__default.createElement(
98751
98752
  SimpleMarkdownEditor,
@@ -98818,7 +98819,7 @@ var SequenceQuestionForm = ({ question: question2, onFormChange }) => {
98818
98819
  }
98819
98820
  return htmlString.replace(/<[^>]*>?/gm, "");
98820
98821
  };
98821
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Sequence Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Sequence Items"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, "Define all items that need to be put in order. Users will arrange these."), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3 mt-2" }, question2.items.map((item, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: item.id, className: "flex items-start space-x-2 p-2 border rounded-md bg-background" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex-grow" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "text-xs font-medium text-muted-foreground" }, "Item ", index3 + 1), /* @__PURE__ */ React169__default.createElement(
98822
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Sequence Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Sequence Items"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, "Define all items that need to be put in order. Users will arrange these."), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3 mt-2" }, question2.items.map((item, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: item.id, className: "flex items-start space-x-2 p-2 border rounded-md bg-background" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex-grow" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "text-xs font-Medium text-muted-foreground" }, "Item ", index3 + 1), /* @__PURE__ */ React169__default.createElement(
98822
98823
  SimpleMarkdownEditor,
98823
98824
  {
98824
98825
  value: item.content,
@@ -98921,7 +98922,7 @@ var MatchingQuestionForm = ({ question: question2, onFormChange }) => {
98921
98922
  }
98922
98923
  return htmlString.replace(/<[^>]*>?/gm, "");
98923
98924
  };
98924
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Matching Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Prompts (Items to be matched)"), question2.prompts.map((promptItem, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: promptItem.id, className: "space-y-1" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "text-xs text-muted-foreground" }, "Prompt ", index3 + 1), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeletePrompt(index3), className: "h-7 w-7 text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__default.createElement(
98925
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Matching Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Prompts (Items to be matched)"), question2.prompts.map((promptItem, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: promptItem.id, className: "space-y-1" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "text-xs text-muted-foreground" }, "Prompt ", index3 + 1), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeletePrompt(index3), className: "h-7 w-7 text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__default.createElement(
98925
98926
  SimpleMarkdownEditor,
98926
98927
  {
98927
98928
  value: promptItem.content,
@@ -99011,7 +99012,7 @@ var DragAndDropQuestionForm = ({ question: question2, onFormChange }) => {
99011
99012
  }
99012
99013
  return htmlString.replace(/<[^>]*>?/gm, "");
99013
99014
  };
99014
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Drag and Drop Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `dnd-bgimage-${question2.id}` }, "Background Image URL (Optional)"), /* @__PURE__ */ React169__default.createElement(
99015
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Drag and Drop Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `dnd-bgimage-${question2.id}` }, "Background Image URL (Optional)"), /* @__PURE__ */ React169__default.createElement(
99015
99016
  Input,
99016
99017
  {
99017
99018
  id: `dnd-bgimage-${question2.id}`,
@@ -99098,7 +99099,7 @@ var HotspotQuestionForm = ({ question: question2, onFormChange }) => {
99098
99099
  }
99099
99100
  onFormChange({ correctHotspotIds: newCorrectIds });
99100
99101
  };
99101
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Hotspot Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `hs-imageurl-${question2.id}` }, "Image URL"), /* @__PURE__ */ React169__default.createElement(
99102
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Hotspot Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `hs-imageurl-${question2.id}` }, "Image URL"), /* @__PURE__ */ React169__default.createElement(
99102
99103
  Input,
99103
99104
  {
99104
99105
  id: `hs-imageurl-${question2.id}`,
@@ -99116,7 +99117,7 @@ var HotspotQuestionForm = ({ question: question2, onFormChange }) => {
99116
99117
  onChange: (e3) => onFormChange({ imageAltText: e3.target.value || void 0 }),
99117
99118
  placeholder: "Describe the image"
99118
99119
  }
99119
- )), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Hotspot Areas"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, "Define clickable areas on the image. Coordinates are relative to the image's top-left corner."), question2.hotspots.map((hotspot, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: hotspot.id, className: "p-3 border rounded-md bg-background space-y-2" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-medium" }, "Hotspot ", index3 + 1, " (ID: ", hotspot.id, ")"), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeleteHotspot(index3), className: "h-8 w-8 text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `hs-shape-${hotspot.id}`, className: "text-xs" }, "Shape"), /* @__PURE__ */ React169__default.createElement(Select2, { value: hotspot.shape, onValueChange: (value) => handleHotspotChange(index3, "shape", value) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: `hs-shape-${hotspot.id}` }, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "rect" }, "Rectangle"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "circle" }, "Circle")))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `hs-coords-${hotspot.id}`, className: "text-xs" }, hotspot.shape === "rect" ? "Coords (x, y, width, height)" : "Coords (centerX, centerY, radius)"), /* @__PURE__ */ React169__default.createElement(
99120
+ )), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Hotspot Areas"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, "Define clickable areas on the image. Coordinates are relative to the image's top-left corner."), question2.hotspots.map((hotspot, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: hotspot.id, className: "p-3 border rounded-md bg-background space-y-2" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-Medium" }, "Hotspot ", index3 + 1, " (ID: ", hotspot.id, ")"), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeleteHotspot(index3), className: "h-8 w-8 text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `hs-shape-${hotspot.id}`, className: "text-xs" }, "Shape"), /* @__PURE__ */ React169__default.createElement(Select2, { value: hotspot.shape, onValueChange: (value) => handleHotspotChange(index3, "shape", value) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: `hs-shape-${hotspot.id}` }, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "rect" }, "Rectangle"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "circle" }, "Circle")))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `hs-coords-${hotspot.id}`, className: "text-xs" }, hotspot.shape === "rect" ? "Coords (x, y, width, height)" : "Coords (centerX, centerY, radius)"), /* @__PURE__ */ React169__default.createElement(
99120
99121
  Input,
99121
99122
  {
99122
99123
  id: `hs-coords-${hotspot.id}`,
@@ -99148,7 +99149,7 @@ var BlocklyProgrammingQuestionForm = ({ question: question2, onFormChange }) =>
99148
99149
  const handleFieldChange = (field, value) => {
99149
99150
  onFormChange({ [field]: value });
99150
99151
  };
99151
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Blockly Programming Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `blockly-toolbox-${question2.id}` }, "Toolbox Definition (XML)"), /* @__PURE__ */ React169__default.createElement(
99152
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Blockly Programming Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `blockly-toolbox-${question2.id}` }, "Toolbox Definition (XML)"), /* @__PURE__ */ React169__default.createElement(
99152
99153
  Textarea,
99153
99154
  {
99154
99155
  id: `blockly-toolbox-${question2.id}`,
@@ -99197,7 +99198,7 @@ var ScratchProgrammingQuestionForm = ({ question: question2, onFormChange }) =>
99197
99198
  const handleFieldChange = (field, value) => {
99198
99199
  onFormChange({ [field]: value });
99199
99200
  };
99200
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Scratch Programming Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "scratch-toolbox-" + question2.id }, "Toolbox Definition (XML for Blockly)"), /* @__PURE__ */ React169__default.createElement(
99201
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Scratch Programming Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "scratch-toolbox-" + question2.id }, "Toolbox Definition (XML for Blockly)"), /* @__PURE__ */ React169__default.createElement(
99201
99202
  Textarea,
99202
99203
  {
99203
99204
  id: "scratch-toolbox-" + question2.id,
@@ -99274,7 +99275,7 @@ var CodingQuestionForm = ({ question: question2, onFormChange }) => {
99274
99275
  const newTestCases = question2.testCases.filter((_2, i2) => i2 !== index3);
99275
99276
  onFormChange({ testCases: newTestCases });
99276
99277
  };
99277
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-medium text-md" }, "Coding Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `coding-lang-${question2.id}` }, "Programming Language"), /* @__PURE__ */ React169__default.createElement(
99278
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-Medium text-md" }, "Coding Question Specifics"), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `coding-lang-${question2.id}` }, "Programming Language"), /* @__PURE__ */ React169__default.createElement(
99278
99279
  Select2,
99279
99280
  {
99280
99281
  value: question2.codingLanguage,
@@ -99300,7 +99301,7 @@ var CodingQuestionForm = ({ question: question2, onFormChange }) => {
99300
99301
  placeholder: "Enter the complete, correct code solution here.",
99301
99302
  className: "min-h-[200px] font-mono text-xs"
99302
99303
  }
99303
- )), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Test Cases"), question2.testCases.map((tc, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: tc.id, className: "p-3 border rounded-md bg-background space-y-2" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-medium" }, "Test Case ", index3 + 1), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeleteTestCase(index3), className: "h-8 w-8 text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `tc-input-${tc.id}`, className: "text-xs" }, "Input (JSON Array)"), /* @__PURE__ */ React169__default.createElement(
99304
+ )), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Test Cases"), question2.testCases.map((tc, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: tc.id, className: "p-3 border rounded-md bg-background space-y-2" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-Medium" }, "Test Case ", index3 + 1), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeleteTestCase(index3), className: "h-8 w-8 text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `tc-input-${tc.id}`, className: "text-xs" }, "Input (JSON Array)"), /* @__PURE__ */ React169__default.createElement(
99304
99305
  Input,
99305
99306
  {
99306
99307
  id: `tc-input-${tc.id}`,
@@ -99468,7 +99469,7 @@ var EditQuestionModal = ({
99468
99469
  if (!isOpen || !editedQuestion) return null;
99469
99470
  return /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => {
99470
99471
  if (!open) onClose();
99471
- } }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-[600px] md:max-w-[800px] lg:max-w-[1000px] max-h-[90vh]" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, { className: "font-headline text-2xl" }, questionData?.id && !questionData.id.startsWith("new_") && !questionData.id.startsWith("temp_") ? "Edit Question" : "Add New Question"), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, "Configure the details for this question. Current type:", " ", /* @__PURE__ */ React169__default.createElement("span", { className: "font-semibold" }, editedQuestion.questionType))), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "max-h-[calc(80vh-150px)] p-1 pr-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "prompt", className: "font-semibold" }, "Question Prompt"), /* @__PURE__ */ React169__default.createElement(SimpleMarkdownEditor, { value: editedQuestion.prompt, onChange: (htmlContent) => handleSpecificFieldChange({ prompt: htmlContent }) })), renderSpecificForm(), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "points" }, "Points"), /* @__PURE__ */ React169__default.createElement(Input, { id: "points", type: "number", value: editedQuestion.points || 0, onChange: (e3) => handleSpecificFieldChange({ points: parseInt(e3.target.value, 10) || 0 }), min: "0" })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "difficulty" }, "Difficulty"), /* @__PURE__ */ React169__default.createElement(Select2, { value: editedQuestion.difficulty || "medium", onValueChange: (value) => handleSpecificFieldChange({ difficulty: value }) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "easy" }, "Easy"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "medium" }, "Medium"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "hard" }, "Hard"))))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "explanation" }, "Explanation (Optional)"), /* @__PURE__ */ React169__default.createElement(SimpleMarkdownEditor, { value: editedQuestion.explanation || "", onChange: (htmlContent) => handleSpecificFieldChange({ explanation: htmlContent }), minHeight: "100px" })), /* @__PURE__ */ React169__default.createElement("details", { className: "group", open: hasDropdownMetadata }, /* @__PURE__ */ React169__default.createElement("summary", { className: "cursor-pointer font-semibold text-primary hover:underline" }, "Advanced Metadata (Optional)"), renderMetadataFields()))), /* @__PURE__ */ React169__default.createElement(DialogFooter, { className: "pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline" }, "Cancel")), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", onClick: handleSaveClick }, /* @__PURE__ */ React169__default.createElement(Save, { className: "mr-2 h-4 w-4" }), " Save Question"))));
99472
+ } }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-[600px] md:max-w-[800px] lg:max-w-[1000px] max-h-[90vh]" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, { className: "font-headline text-2xl" }, questionData?.id && !questionData.id.startsWith("new_") && !questionData.id.startsWith("temp_") ? "Edit Question" : "Add New Question"), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, "Configure the details for this question. Current type:", " ", /* @__PURE__ */ React169__default.createElement("span", { className: "font-semibold" }, editedQuestion.questionType))), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "max-h-[calc(80vh-150px)] p-1 pr-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6 p-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "prompt", className: "font-semibold" }, "Question Prompt"), /* @__PURE__ */ React169__default.createElement(SimpleMarkdownEditor, { value: editedQuestion.prompt, onChange: (htmlContent) => handleSpecificFieldChange({ prompt: htmlContent }) })), renderSpecificForm(), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "points" }, "Points"), /* @__PURE__ */ React169__default.createElement(Input, { id: "points", type: "number", value: editedQuestion.points || 0, onChange: (e3) => handleSpecificFieldChange({ points: parseInt(e3.target.value, 10) || 0 }), min: "0" })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "difficulty" }, "Difficulty"), /* @__PURE__ */ React169__default.createElement(Select2, { value: editedQuestion.difficulty || "Medium", onValueChange: (value) => handleSpecificFieldChange({ difficulty: value }) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "Easy" }, "Easy"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "Medium" }, "Medium"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "Hard" }, "Hard"))))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "explanation" }, "Explanation (Optional)"), /* @__PURE__ */ React169__default.createElement(SimpleMarkdownEditor, { value: editedQuestion.explanation || "", onChange: (htmlContent) => handleSpecificFieldChange({ explanation: htmlContent }), minHeight: "100px" })), /* @__PURE__ */ React169__default.createElement("details", { className: "group", open: hasDropdownMetadata }, /* @__PURE__ */ React169__default.createElement("summary", { className: "cursor-pointer font-semibold text-primary hover:underline" }, "Advanced Metadata (Optional)"), renderMetadataFields()))), /* @__PURE__ */ React169__default.createElement(DialogFooter, { className: "pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline" }, "Cancel")), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", onClick: handleSaveClick }, /* @__PURE__ */ React169__default.createElement(Save, { className: "mr-2 h-4 w-4" }), " Save Question"))));
99472
99473
  };
99473
99474
 
99474
99475
  // src/react-ui/components/authoring/AIQuestionGeneratorModal.tsx
@@ -99618,12 +99619,12 @@ var QuizContextSchema = z.object({
99618
99619
  originalSubject: z.string().optional(),
99619
99620
  originalCategory: z.string().optional(),
99620
99621
  originalTopic: z.string().optional(),
99621
- loDescription: z.string().optional().describe("The full description of the learning objective for deep context."),
99622
+ description: z.string().optional().describe("The full description of the learning objective for deep context."),
99622
99623
  gradeBand: z.string().optional()
99623
99624
  });
99624
99625
  var BaseQuestionGenerationClientInputSchema = z.object({
99625
99626
  language: z.string().optional().default("English"),
99626
- difficulty: z.enum(["easy", "medium", "hard"]),
99627
+ difficulty: z.enum(["Easy", "Medium", "Hard"]),
99627
99628
  quizContext: QuizContextSchema.optional(),
99628
99629
  imageUrl: z.string().url().optional().describe("Optional URL of an image to be used as context.")
99629
99630
  });
@@ -99632,7 +99633,7 @@ var BaseQuestionZodSchema = z.object({
99632
99633
  prompt: z.string().min(1),
99633
99634
  points: z.number().min(0).optional(),
99634
99635
  explanation: z.string().optional(),
99635
- difficulty: z.enum(["easy", "medium", "hard"]).optional(),
99636
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
99636
99637
  topic: z.string().optional(),
99637
99638
  category: z.string().optional(),
99638
99639
  subject: z.string().optional(),
@@ -99728,7 +99729,7 @@ var AITrueFalseOutputFieldsSchema = z.object({
99728
99729
  correctAnswer: z.boolean(),
99729
99730
  explanation: z.string().optional().describe("An explanation of why the statement is true or false, especially important if false."),
99730
99731
  points: z.number().optional().default(10),
99731
- difficulty: z.enum(["easy", "medium", "hard"]).optional(),
99732
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
99732
99733
  topic: z.string().optional(),
99733
99734
  verifiedCategory: z.string().optional()
99734
99735
  // Thêm để xác thực
@@ -99749,7 +99750,7 @@ Previous attempts failed. Ensure the JSON is valid and 'correctAnswer' is a bool
99749
99750
  const misconceptionGuidance = quizContext?.targetMisconception ? `**Target Misconception:** The statement you create MUST be FALSE and based on this common mistake: "${quizContext.targetMisconception}"` : "";
99750
99751
  const contextStrings = [
99751
99752
  `**Required Category:** ${category}`,
99752
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
99753
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
99753
99754
  imageContextInstruction,
99754
99755
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
99755
99756
  misconceptionGuidance,
@@ -99760,7 +99761,7 @@ Previous attempts failed. Ensure the JSON is valid and 'correctAnswer' is a bool
99760
99761
  correctAnswer: true,
99761
99762
  explanation: "Optional values in Swift represent the presence or absence of a value. To access the value when it exists, you must unwrap it using methods like 'if let', 'guard let', or the force unwrap operator '!'.",
99762
99763
  points: 10,
99763
- difficulty: "easy",
99764
+ difficulty: "Easy",
99764
99765
  topic: "Swift Optionals",
99765
99766
  verifiedCategory: category
99766
99767
  }, null, 2);
@@ -99896,7 +99897,7 @@ var AIMCQOutputFieldsSchema = z.object({
99896
99897
  correctTempOptionId: z.string().describe("The temporary ID of the correct option from the generated options array."),
99897
99898
  explanation: z.string().optional().describe("A brief explanation of why the answer is correct."),
99898
99899
  points: z.number().optional().default(10),
99899
- difficulty: z.enum(["easy", "medium", "hard"]).optional(),
99900
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
99900
99901
  topic: z.string().optional(),
99901
99902
  verifiedCategory: z.string().optional().describe("The category this question actually addresses.")
99902
99903
  });
@@ -99915,7 +99916,7 @@ Previous attempts failed...
99915
99916
  const imageContextInstruction = imageUrl ? `**Image Context:** You MUST analyze the provided image. The question and options must be directly related to the content of this image.` : "";
99916
99917
  const contextStrings = [
99917
99918
  `**Required Category:** ${category}`,
99918
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
99919
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
99919
99920
  imageContextInstruction,
99920
99921
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
99921
99922
  quizContext?.targetMisconception && `**Target Misconception:** Use this to create plausible incorrect answers: "${quizContext.targetMisconception}"`,
@@ -99932,7 +99933,7 @@ Previous attempts failed...
99932
99933
  correctTempOptionId: "C",
99933
99934
  explanation: `The 'guard' statement in ${category} provides an early exit from a scope (like a function) if a condition is false, enhancing code readability by handling required conditions upfront.`,
99934
99935
  points: 10,
99935
- difficulty: "easy",
99936
+ difficulty: "Easy",
99936
99937
  topic: `Control Flow in ${category}`,
99937
99938
  verifiedCategory: category
99938
99939
  }, null, 2);
@@ -100064,807 +100065,6 @@ async function generateMCQQuestion(clientInput, apiKey) {
100064
100065
  return { error: errorMessage };
100065
100066
  }
100066
100067
 
100067
- // src/react-ui/components/authoring/AIQuestionGeneratorModal.tsx
100068
- var supportedQuestionTypesForAI = [
100069
- { value: "true_false", label: "True/False" },
100070
- { value: "multiple_choice", label: "Multiple Choice" },
100071
- { value: "multiple_response", label: "Multiple Response" },
100072
- { value: "short_answer", label: "Short Answer" },
100073
- { value: "numeric", label: "Numeric" },
100074
- { value: "fill_in_the_blanks", label: "Fill In The Blanks" },
100075
- { value: "sequence", label: "Sequence" },
100076
- { value: "matching", label: "Matching" }
100077
- ];
100078
- var AIQuestionGeneratorModal = ({
100079
- isOpen,
100080
- onClose,
100081
- onQuestionGenerated,
100082
- language: language3,
100083
- questionType: questionTypeProp,
100084
- subjects = [],
100085
- topics = [],
100086
- gradeLevels = [],
100087
- bloomLevels = []
100088
- }) => {
100089
- const [prompt, setPrompt] = useState("");
100090
- const [isLoading, setIsLoading] = useState(false);
100091
- const [error, setError] = useState(null);
100092
- const { toast: toast2 } = useToast();
100093
- const [subjectCode, setSubjectCode] = useState("");
100094
- const [topicCode, setTopicCode] = useState("");
100095
- const [gradeBand, setGradeBand] = useState("");
100096
- const [bloomLevelCode, setBloomLevelCode] = useState("");
100097
- const [selectedQuestionType, setSelectedQuestionType] = useState("multiple_choice");
100098
- const [isApiKeyManagerModalOpen, setIsApiKeyManagerModalOpen] = useState(false);
100099
- const [geminiApiKeyExists, setGeminiApiKeyExists] = useState(false);
100100
- const finalQuestionType = questionTypeProp || selectedQuestionType;
100101
- useEffect(() => {
100102
- if (isOpen) {
100103
- setPrompt("");
100104
- setError(null);
100105
- setIsLoading(false);
100106
- setSubjectCode("");
100107
- setTopicCode("");
100108
- setGradeBand("");
100109
- setBloomLevelCode("");
100110
- setSelectedQuestionType(questionTypeProp || "multiple_choice");
100111
- setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
100112
- }
100113
- }, [isOpen, questionTypeProp]);
100114
- const filteredTopics = useMemo(() => {
100115
- if (!subjectCode) return [];
100116
- return topics.filter((t4) => t4.subjectCode === subjectCode);
100117
- }, [subjectCode, topics]);
100118
- const handleApiKeyModalClose = () => {
100119
- setIsApiKeyManagerModalOpen(false);
100120
- setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
100121
- };
100122
- const handleSubmit = async () => {
100123
- if (!prompt.trim()) {
100124
- setError("Please provide a prompt for the question.");
100125
- return;
100126
- }
100127
- const geminiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
100128
- if (!geminiKey) {
100129
- setError("Gemini API Key is not set. Please configure it.");
100130
- setGeminiApiKeyExists(false);
100131
- return;
100132
- }
100133
- setGeminiApiKeyExists(true);
100134
- setError(null);
100135
- setIsLoading(true);
100136
- try {
100137
- const quizContext = {
100138
- plannedTopic: prompt,
100139
- originalSubject: subjectCode,
100140
- originalTopic: topicCode,
100141
- gradeBand,
100142
- plannedBloomLevel: bloomLevelCode
100143
- };
100144
- const baseClientInput = {
100145
- language: language3,
100146
- difficulty: "medium",
100147
- quizContext
100148
- };
100149
- let generatedResult = {};
100150
- switch (finalQuestionType) {
100151
- case "true_false":
100152
- generatedResult = await generateTrueFalseQuestion(baseClientInput, geminiKey);
100153
- break;
100154
- case "multiple_choice":
100155
- generatedResult = await generateMCQQuestion({ ...baseClientInput, numberOfOptions: 4 }, geminiKey);
100156
- break;
100157
- // Add other cases as needed
100158
- default:
100159
- throw new Error(`AI generation for '${finalQuestionType}' is not implemented in this flow.`);
100160
- }
100161
- if (generatedResult.error) {
100162
- throw new Error(generatedResult.error);
100163
- }
100164
- if (generatedResult.question) {
100165
- const completeQuestion = generatedResult.question;
100166
- completeQuestion.subject = subjectCode;
100167
- completeQuestion.topic = topicCode;
100168
- completeQuestion.gradeBand = gradeBand;
100169
- completeQuestion.bloomLevel = bloomLevelCode;
100170
- if (completeQuestion.points === void 0) completeQuestion.points = 10;
100171
- onQuestionGenerated(completeQuestion);
100172
- toast2({ title: "AI Question Generated", description: "Review and edit the generated question." });
100173
- onClose();
100174
- } else {
100175
- throw new Error("AI did not return a valid question object.");
100176
- }
100177
- } catch (e3) {
100178
- console.error("AI Question Generation Error:", e3);
100179
- let errorMessage = e3.message || "An unknown error occurred.";
100180
- if (typeof errorMessage === "string" && errorMessage.includes("API key not valid")) {
100181
- errorMessage = "The provided Google Gemini API Key is invalid. Please check it.";
100182
- setGeminiApiKeyExists(false);
100183
- }
100184
- setError(errorMessage);
100185
- toast2({ title: "AI Generation Failed", description: errorMessage, variant: "destructive" });
100186
- } finally {
100187
- setIsLoading(false);
100188
- }
100189
- };
100190
- const comboboxOptions = {
100191
- subjects: subjects.map((s4) => ({ value: s4.code, label: s4.name })),
100192
- topics: filteredTopics.map((t4) => ({ value: t4.code, label: t4.name })),
100193
- gradeLevels: gradeLevels.map((g) => ({ value: g.code, label: g.name })),
100194
- bloomLevels: bloomLevels.map((b2) => ({ value: b2.code, label: b2.name }))
100195
- };
100196
- return /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => {
100197
- if (!open) onClose();
100198
- } }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-[600px]" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, { className: "flex items-center font-headline text-2xl" }, /* @__PURE__ */ React169__default.createElement(WandSparkles, { className: "mr-2 h-6 w-6 text-primary" }), " AI Question Generator"), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, "Provide a prompt and metadata to generate a '", finalQuestionType, "' question.")), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 py-4 max-h-[60vh] overflow-y-auto px-2" }, !geminiApiKeyExists && /* @__PURE__ */ React169__default.createElement("div", { className: "p-3 mb-4 border border-amber-500 bg-amber-50 rounded-md text-amber-700" }), !questionTypeProp && /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "ai-q-type-select" }, "Question Type"), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedQuestionType, onValueChange: (v) => setSelectedQuestionType(v) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "ai-q-type-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, supportedQuestionTypesForAI.map((type) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: type.value, value: type.value }, type.label))))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "ai-prompt" }, "Prompt / Topic"), /* @__PURE__ */ React169__default.createElement(
100199
- Textarea,
100200
- {
100201
- id: "ai-prompt",
100202
- value: prompt,
100203
- onChange: (e3) => setPrompt(e3.target.value),
100204
- placeholder: "e.g., The process of photosynthesis, The causes of World War II, Basic Algebra Equations",
100205
- className: "min-h-[100px]"
100206
- }
100207
- )), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, null, "Subject"), /* @__PURE__ */ React169__default.createElement(EditableCombobox, { options: comboboxOptions.subjects, value: subjectCode, onChange: setSubjectCode, placeholder: "Select or type a Subject..." })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, null, "Topic"), /* @__PURE__ */ React169__default.createElement(EditableCombobox, { options: comboboxOptions.topics, value: topicCode, onChange: setTopicCode, placeholder: "Select or type a Topic...", disabled: !subjectCode })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, null, "Grade Level"), /* @__PURE__ */ React169__default.createElement(EditableCombobox, { options: comboboxOptions.gradeLevels, value: gradeBand, onChange: setGradeBand, placeholder: "Select or type a Grade..." })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, null, "Bloom's Level"), /* @__PURE__ */ React169__default.createElement(EditableCombobox, { options: comboboxOptions.bloomLevels, value: bloomLevelCode, onChange: setBloomLevelCode, placeholder: "Select a Bloom's Level..." }))), error && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error)), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline" }, "Cancel")), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", onClick: handleSubmit, disabled: isLoading || !prompt.trim() || !geminiApiKeyExists }, isLoading ? /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), "Generate Question")))), /* @__PURE__ */ React169__default.createElement(APIKeyManagerModal, { isOpen: isApiKeyManagerModalOpen, onClose: handleApiKeyModalClose }));
100208
- };
100209
-
100210
- // src/react-ui/components/authoring/AIFullQuizGeneratorModal.tsx
100211
- init_react_shim();
100212
-
100213
- // src/ai/flows/generate-quiz-plan.ts
100214
- init_react_shim();
100215
-
100216
- // src/ai/flows/generate-quiz-plan-types.ts
100217
- init_react_shim();
100218
- var TopicWithMetadataSchema = z.object({
100219
- topic: z.string().min(1),
100220
- ratio: z.number().min(0).max(100),
100221
- originalLoId: z.string().optional(),
100222
- originalSubject: z.string().optional(),
100223
- originalCategory: z.string().optional(),
100224
- originalTopic: z.string().optional(),
100225
- commonMisconceptions: z.array(z.string()).optional()
100226
- });
100227
- var BloomLevelStringsEnum = z.enum(["remembering", "understanding", "applying", "analyzing", "evaluating", "creating"]);
100228
- var fullQuizSupportedQuestionTypesArray = [
100229
- "true_false",
100230
- "multiple_choice",
100231
- "multiple_response",
100232
- "short_answer",
100233
- "numeric",
100234
- "fill_in_the_blanks",
100235
- "sequence",
100236
- "matching",
100237
- "drag_and_drop",
100238
- "coding"
100239
- ];
100240
- z.object({
100241
- language: z.string().optional().default("English"),
100242
- totalQuestions: z.number().int().min(1).max(50),
100243
- numCodingQuestions: z.number().optional().default(0),
100244
- topics: z.array(TopicWithMetadataSchema).min(1),
100245
- bloomLevels: z.array(z.object({
100246
- level: BloomLevelStringsEnum,
100247
- ratio: z.number().min(0).max(100)
100248
- })).min(1),
100249
- selectedContextIds: z.array(z.string()).optional(),
100250
- selectedQuestionTypes: z.array(z.enum(fullQuizSupportedQuestionTypesArray)).min(1),
100251
- imageContexts: z.array(z.custom()).optional().describe("Library of available image contexts for the AI to use.")
100252
- });
100253
- var PlannedQuestionSchema = z.object({
100254
- plannedTopic: z.string().min(1).describe("The specific, assessable topic for this question."),
100255
- plannedQuestionType: z.enum(fullQuizSupportedQuestionTypesArray).describe("The specific question type chosen."),
100256
- plannedBloomLevel: BloomLevelStringsEnum.describe("The Bloom's level assigned."),
100257
- plannedContextId: z.string().optional().describe("The specific context ID chosen for this question."),
100258
- imageId: z.string().nullable().optional().describe("The ID of the image from the context library to be used for this question."),
100259
- targetMisconception: z.string().optional().describe("A specific common misconception this question should target."),
100260
- difficultyReason: z.string().optional().describe("Strategic explanation of difficulty choice and placement."),
100261
- topicSpecificity: z.enum(["broad", "focused", "specific"]).optional().describe("How specific the topic coverage should be."),
100262
- originalLoId: z.string().optional(),
100263
- originalSubject: z.string().optional(),
100264
- originalCategory: z.string().optional(),
100265
- originalTopic: z.string().optional()
100266
- });
100267
- var GenerateQuizPlanOutputSchema = z.object({
100268
- quizPlan: z.array(PlannedQuestionSchema).describe("A detailed plan for each question in the quiz."),
100269
- diversityMetrics: z.object({
100270
- questionTypeDistribution: z.record(z.number()).optional(),
100271
- bloomLevelDistribution: z.record(z.number()).optional(),
100272
- maxConsecutiveSameType: z.number().optional()
100273
- }).optional().describe("Metrics showing the diversity achieved in the plan."),
100274
- planningStrategy: z.object({
100275
- overallApproach: z.string().optional(),
100276
- keyDecisions: z.array(z.string()).optional()
100277
- }).optional()
100278
- });
100279
-
100280
- // src/ai/flows/generate-quiz-plan.ts
100281
- var QuizPlanLogger = class {
100282
- constructor() {
100283
- this.logs = [];
100284
- this.startTime = Date.now();
100285
- }
100286
- log(phase, data, duration) {
100287
- this.logs.push({
100288
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
100289
- phase,
100290
- data,
100291
- duration
100292
- });
100293
- if (duration !== void 0) {
100294
- console.log(`[${phase}] Completed in ${duration}ms:`, data);
100295
- } else {
100296
- console.log(`[${phase}]:`, data);
100297
- }
100298
- }
100299
- getLogs() {
100300
- return this.logs;
100301
- }
100302
- getTotalDuration() {
100303
- return Date.now() - this.startTime;
100304
- }
100305
- };
100306
- function generateQuestionTypeSelectionGuidance() {
100307
- return `
100308
- QUESTION TYPE SELECTION BEST PRACTICES:
100309
-
100310
- **TRUE_FALSE (true_false)**
100311
- - Best for: Binary concepts, fact verification, common misconceptions
100312
- - Bloom levels: Primarily Remembering, Understanding
100313
- - Use when: Testing definitive statements, clarifying misconceptions
100314
- - Example: "Photosynthesis only occurs during daytime" (targets timing misconception)
100315
-
100316
- **MULTIPLE CHOICE (multiple_choice)**
100317
- - Best for: Concept selection, process understanding, comparison
100318
- - Bloom levels: All levels, especially Understanding and Applying
100319
- - Use when: Testing conceptual understanding with clear alternatives
100320
- - Example: "Which factor most affects enzyme activity?" (applying knowledge)
100321
-
100322
- **MULTIPLE RESPONSE (multiple_response)**
100323
- - Best for: Identifying multiple correct factors, comprehensive understanding
100324
- - Bloom levels: Understanding, Analyzing, Evaluating
100325
- - Use when: Multiple correct answers exist, testing thorough knowledge
100326
- - Example: "Select all factors that influence plant growth" (analyzing components)
100327
-
100328
- **FILL IN THE BLANKS (fill_in_the_blanks)**
100329
- - Best for: Key terminology, formulas, specific facts
100330
- - Bloom levels: Remembering, Understanding
100331
- - Use when: Testing precise recall of important terms/concepts
100332
- - Example: "The process of _____ converts light energy to chemical energy"
100333
-
100334
- **NUMERIC (numeric)**
100335
- - Best for: Calculations, quantitative problems, formula application
100336
- - Bloom levels: Applying, Analyzing
100337
- - Use when: Mathematical computations are required
100338
- - Example: "Calculate the molarity of a 2L solution containing 0.5 moles of NaCl"
100339
-
100340
- **MATCHING (matching)**
100341
- - Best for: Connecting related concepts, terminology pairs
100342
- - Bloom levels: Remembering, Understanding
100343
- - Use when: Testing relationships between terms and definitions
100344
- - Example: Match organelles with their functions
100345
-
100346
- **SEQUENCE (sequence)**
100347
- - Best for: Process steps, chronological order, procedural knowledge
100348
- - Bloom levels: Understanding, Applying, Analyzing
100349
- - Use when: Order or sequence is critical to understanding
100350
- - Example: "Arrange the steps of mitosis in correct order"
100351
-
100352
- **DRAG AND DROP (drag_and_drop)**
100353
- - Best for: Categorization, classification, spatial relationships
100354
- - Bloom levels: Understanding, Applying, Analyzing
100355
- - Use when: Grouping or organizing information is key
100356
- - Example: "Classify these compounds as acids, bases, or neutral"
100357
-
100358
- **SHORT ANSWER (short_answer)**
100359
- - Best for: Explanations, definitions, problem-solving steps
100360
- - Bloom levels: Understanding, Applying, Analyzing, Evaluating, Creating
100361
- - Use when: Requiring explanatory responses, open-ended thinking
100362
- - Example: "Explain why enzymes are specific to certain substrates"
100363
-
100364
- **CODING (coding)**
100365
- - Best for: Programming problems, algorithm implementation
100366
- - Bloom levels: Applying, Analyzing, Evaluating, Creating
100367
- - Use when: Technical implementation or code analysis is required
100368
- - Example: "Write a function to calculate factorial recursively"
100369
- `;
100370
- }
100371
- function generateAdvancedBloomGuidance() {
100372
- return `
100373
- ADVANCED BLOOM'S TAXONOMY & QUESTION TYPE OPTIMIZATION:
100374
-
100375
- **REMEMBERING (Knowledge Recall)**
100376
- - Primary types: true_false, fill_in_the_blanks, matching
100377
- - Secondary types: multiple_choice (simple recall)
100378
- - Focus: Facts, terms, basic concepts, definitions
100379
- - Misconception addressing: Use true_false to clarify common confusions
100380
-
100381
- **UNDERSTANDING (Comprehension)**
100382
- - Primary types: multiple_choice, short_answer, matching
100383
- - Secondary types: multiple_response, sequence
100384
- - Focus: Explanations, interpretations, examples, classifications
100385
- - Best for: "Explain why...", "What does this mean...", "Give an example..."
100386
-
100387
- **APPLYING (Using Knowledge)**
100388
- - Primary types: numeric, short_answer, sequence, coding
100389
- - Secondary types: multiple_choice, drag_and_drop
100390
- - Focus: Problem-solving, implementing procedures, using methods
100391
- - Best for: Calculations, step-by-step processes, practical applications
100392
-
100393
- **ANALYZING (Breaking Down Information)**
100394
- - Primary types: multiple_response, short_answer, sequence, coding
100395
- - Secondary types: drag_and_drop, multiple_choice
100396
- - Focus: Identifying components, relationships, cause-effect
100397
- - Best for: "What are the factors...", "How do these relate...", "Break down..."
100398
-
100399
- **EVALUATING (Making Judgments)**
100400
- - Primary types: short_answer, multiple_response, coding
100401
- - Secondary types: multiple_choice (with justification)
100402
- - Focus: Critiquing, judging, comparing alternatives, decision-making
100403
- - Best for: "Which is better and why...", "Evaluate the approach...", "Critique..."
100404
-
100405
- **CREATING (Producing New Work)**
100406
- - Primary types: short_answer, coding, sequence
100407
- - Secondary types: drag_and_drop (design tasks)
100408
- - Focus: Designing, planning, producing, constructing
100409
- - Best for: "Design a solution...", "Create a plan...", "Develop a strategy..."
100410
- `;
100411
- }
100412
- function generateDiversityRules() {
100413
- return `
100414
- ENHANCED DIVERSITY & QUALITY ASSURANCE RULES:
100415
-
100416
- **Question Type Distribution Strategy:**
100417
- 1. Never place more than 3 consecutive questions of the same type
100418
- 2. Distribute question types based on their cognitive complexity
100419
- 3. For quizzes with 10+ questions, use at least 4 different question types
100420
- 4. For quizzes with 20+ questions, use at least 6 different question types
100421
- 5. Balance quick-answer types (true_false, multiple_choice) with deeper types (short_answer, coding)
100422
-
100423
- **Intelligent Difficulty Progression:**
100424
- 1. Opening (20%): Start with confidence-building questions (Remembering/Understanding)
100425
- 2. Building (40%): Gradually increase complexity (Understanding/Applying)
100426
- 3. Peak (30%): Most challenging questions (Analyzing/Evaluating/Creating)
100427
- 4. Closing (10%): Moderate challenge to end positively
100428
-
100429
- **Misconception-Driven Planning:**
100430
- - When common misconceptions are provided, prioritize question types that can effectively address them
100431
- - Use true_false for binary misconceptions
100432
- - Use multiple_choice for concept selection misconceptions
100433
- - Use short_answer for complex misconceptions requiring explanation
100434
-
100435
- **Contextual Intelligence:**
100436
- - Programming/Technical topics: Favor coding, numeric, short_answer
100437
- - Process-oriented topics: Favor sequence, short_answer, drag_and_drop
100438
- - Conceptual topics: Favor multiple_choice, multiple_response, true_false
100439
- - Factual topics: Favor fill_in_the_blanks, matching, true_false
100440
- `;
100441
- }
100442
- async function generateQuizPlan(clientInput, apiKey, imageContexts = []) {
100443
- const logger = new QuizPlanLogger();
100444
- try {
100445
- logger.log("VALIDATION_START", {
100446
- totalQuestions: clientInput.totalQuestions,
100447
- availableTypes: clientInput.selectedQuestionTypes,
100448
- topicCount: clientInput.topics.length,
100449
- bloomLevelCount: clientInput.bloomLevels.length
100450
- });
100451
- const totalTopicRatio = clientInput.topics.reduce((sum, t4) => sum + t4.ratio, 0);
100452
- if (Math.abs(totalTopicRatio - 100) > 1) {
100453
- throw new Error(`Total topic ratio must be 100%. Current sum: ${totalTopicRatio.toFixed(1)}%`);
100454
- }
100455
- const totalBloomRatio = clientInput.bloomLevels.reduce((sum, b2) => sum + b2.ratio, 0);
100456
- if (Math.abs(totalBloomRatio - 100) > 1) {
100457
- throw new Error(`Total Bloom level ratio must be 100%. Current sum: ${totalBloomRatio.toFixed(1)}%`);
100458
- }
100459
- logger.log("VALIDATION_SUCCESS", {
100460
- topicRatioSum: totalTopicRatio,
100461
- bloomRatioSum: totalBloomRatio
100462
- });
100463
- const aiStartTime = Date.now();
100464
- const ai = new GoogleGenAI({
100465
- apiKey
100466
- });
100467
- const model = "gemini-2.5-pro";
100468
- const config3 = {
100469
- temperature: 0.8,
100470
- thinkingConfig: {
100471
- thinkingBudget: 4096
100472
- },
100473
- responseMimeType: "application/json"
100474
- };
100475
- logger.log("AI_INITIALIZATION", { model }, Date.now() - aiStartTime);
100476
- const { language: language3, totalQuestions, numCodingQuestions = 0 } = clientInput;
100477
- const promptStartTime = Date.now();
100478
- const topicsDistribution = clientInput.topics.map((t4) => {
100479
- let topicString = `- Topic Context: "${t4.topic}", LoId: "${t4.originalLoId || "nil"}", Subject: "${t4.originalSubject || "nil"}", Category: "${t4.originalCategory || "nil"}", Topic: "${t4.originalTopic || "nil"}", Ratio: ${t4.ratio}%`;
100480
- if (t4.commonMisconceptions && t4.commonMisconceptions.length > 0) {
100481
- topicString += `
100482
- - Common Misconceptions: [${t4.commonMisconceptions.join(", ")}]`;
100483
- }
100484
- return topicString;
100485
- }).join("\n ");
100486
- const bloomDistribution = clientInput.bloomLevels.map(
100487
- (b2) => `- Level: "${b2.level}", Ratio: ${b2.ratio}%`
100488
- ).join("\n ");
100489
- let questionTypesForPrompt = [...clientInput.selectedQuestionTypes];
100490
- if (numCodingQuestions === 0) {
100491
- questionTypesForPrompt = questionTypesForPrompt.filter(
100492
- (type) => type !== "short_answer" && type !== "coding"
100493
- );
100494
- }
100495
- const allowedQuestionTypes = questionTypesForPrompt.map((t4) => `'${t4}'`).join(", ");
100496
- const codingRequirement = numCodingQuestions > 0 ? `
100497
- **CRITICAL CODING REQUIREMENT**: Exactly ${numCodingQuestions} questions in the plan MUST be of type 'coding'. These should typically be at 'applying' or higher Bloom levels and focus on implementation, debugging, or algorithm design.` : "";
100498
- const imageContextSection = imageContexts && imageContexts.length > 0 ? `
100499
- ## AVAILABLE IMAGE CONTEXT LIBRARY
100500
- You have access to a library of pre-described images. When planning a question, if its subject, category, and topic match an image in this library AND the image's description is relevant to the planned question's topic, you MUST include the corresponding \`imageId\` in your output. Otherwise, leave the \`imageId\` field null or omit it.
100501
-
100502
- \`\`\`json
100503
- ${JSON.stringify(imageContexts.map((img) => ({ imageId: img.id, subject: img.subject, category: img.category, topic: img.topic, description: img.detailedDescription })), null, 2)}
100504
- \`\`\`
100505
- ` : "";
100506
- const enhancedPromptText = `You are an elite educational assessment architect with expertise in cognitive science and adaptive learning. Your mission is to create a strategically optimized quiz plan that maximizes learning effectiveness.
100507
-
100508
- ${generateQuestionTypeSelectionGuidance()}
100509
-
100510
- ${generateAdvancedBloomGuidance()}
100511
-
100512
- ${generateDiversityRules()}
100513
-
100514
- ## COMPREHENSIVE QUIZ REQUIREMENTS:
100515
-
100516
- **Target Language**: ${language3}
100517
- **Total Questions**: ${totalQuestions}${codingRequirement}
100518
-
100519
- **Topic Distribution & Learning Context** (follow precisely):
100520
- ${topicsDistribution}
100521
-
100522
- **Cognitive Complexity Distribution** (follow precisely):
100523
- ${bloomDistribution}
100524
-
100525
- **Available Question Arsenal**: ${allowedQuestionTypes}
100526
-
100527
- **Image Resources**: ${imageContextSection}
100528
-
100529
- ## STRATEGIC PLANNING METHODOLOGY:
100530
-
100531
- 1. **Misconception Analysis**: If common misconceptions are provided, design questions specifically to address and correct them
100532
- 2. **Question Type Intelligence**: Select question types based on the cognitive demands and content nature
100533
- 3. **Difficulty Orchestration**: Create a learning journey that builds confidence while challenging appropriately
100534
- 4. **Diversity Optimization**: Ensure variety prevents monotony and maintains engagement
100535
- 5. **Context Sensitivity**: Match question types to topic characteristics (technical, conceptual, procedural)
100536
- 6. **Image Context Integration**: Intelligently associate questions with relevant images from the provided library to enhance contextual understanding.
100537
-
100538
- ## ENHANCED OUTPUT FORMAT:
100539
-
100540
- Return ONLY a JSON object with this EXACT structure:
100541
-
100542
- \`\`\`json
100543
- {
100544
- "quizPlan": [
100545
- {
100546
- "plannedTopic": "Specific, assessable topic derived from provided context",
100547
- "plannedQuestionType": "question_type_from_allowed_list",
100548
- "plannedBloomLevel": "bloom_level_from_requirements",
100549
- "plannedContextId": "THEO_ABS",
100550
- "imageId": "imgctx_12345abcde", // or null
100551
- "targetMisconception": "Specific misconception this question addresses (or 'none' if not applicable)",
100552
- "difficultyReason": "Strategic explanation of difficulty choice and placement",
100553
- "topicSpecificity": "broad|focused|specific",
100554
- "originalLoId": "corresponding_LoId_from_input",
100555
- "originalSubject": "corresponding_Subject_from_input",
100556
- "originalCategory": "corresponding_Category_from_input",
100557
- "originalTopic": "corresponding_Topic_from_input"
100558
- }
100559
- ],
100560
- "diversityMetrics": {
100561
- "questionTypeDistribution": {"type1": count1, "type2": count2},
100562
- "bloomLevelDistribution": {"level1": count1, "level2": count2},
100563
- "maxConsecutiveSameType": number,
100564
- "difficultyProgression": "description of how difficulty progresses",
100565
- "misconceptionCoverage": number_of_misconceptions_addressed
100566
- },
100567
- "planningStrategy": {
100568
- "overallApproach": "Brief description of the strategic approach taken",
100569
- "keyDecisions": ["Major decision 1", "Major decision 2", "Major decision 3"]
100570
- }
100571
- }
100572
- \`\`\`
100573
-
100574
- Execute this plan with pedagogical precision. The quiz should feel like a carefully crafted learning journey that challenges and educates simultaneously.`;
100575
- logger.log("PROMPT_PREPARATION", {
100576
- promptLength: enhancedPromptText.length,
100577
- topicCount: clientInput.topics.length,
100578
- misconceptionCount: clientInput.topics.reduce((sum, t4) => sum + (t4.commonMisconceptions?.length || 0), 0)
100579
- }, Date.now() - promptStartTime);
100580
- const generationStartTime = Date.now();
100581
- const contents = [
100582
- {
100583
- role: "user",
100584
- parts: [
100585
- {
100586
- text: enhancedPromptText
100587
- }
100588
- ]
100589
- }
100590
- ];
100591
- const aiResult = await ai.models.generateContent({
100592
- model,
100593
- config: config3,
100594
- contents
100595
- });
100596
- const response = aiResult;
100597
- const generationDuration = Date.now() - generationStartTime;
100598
- logger.log("AI_GENERATION", {
100599
- responseLength: response.candidates?.[0]?.content?.parts?.[0]?.text?.length || 0,
100600
- duration: generationDuration
100601
- }, generationDuration);
100602
- const processingStartTime = Date.now();
100603
- const rawText = response.candidates?.[0]?.content?.parts?.[0]?.text || "";
100604
- let jsonText = rawText;
100605
- if (!rawText.trim().startsWith("{") && !rawText.trim().startsWith("[")) {
100606
- jsonText = extractJsonFromMarkdown(rawText);
100607
- }
100608
- logger.log("JSON_EXTRACTION", {
100609
- rawTextLength: rawText.length,
100610
- extractedJsonLength: jsonText.length
100611
- });
100612
- const aiGeneratedContent = GenerateQuizPlanOutputSchema.parse(JSON.parse(jsonText));
100613
- logger.log("SCHEMA_VALIDATION", { success: true }, Date.now() - processingStartTime);
100614
- const validationStartTime = Date.now();
100615
- if (aiGeneratedContent.quizPlan.length !== clientInput.totalQuestions) {
100616
- throw new Error(`AI planned for ${aiGeneratedContent.quizPlan.length} questions, but ${clientInput.totalQuestions} were requested.`);
100617
- }
100618
- const invalidTypes = [];
100619
- aiGeneratedContent.quizPlan.forEach((item, index3) => {
100620
- if (!clientInput.selectedQuestionTypes.includes(item.plannedQuestionType)) {
100621
- invalidTypes.push(`Question ${index3 + 1}: '${item.plannedQuestionType}'`);
100622
- }
100623
- });
100624
- if (invalidTypes.length > 0) {
100625
- throw new Error(`Invalid question types found: ${invalidTypes.join(", ")}`);
100626
- }
100627
- const codingQuestions = aiGeneratedContent.quizPlan.filter((q2) => q2.plannedQuestionType === "coding");
100628
- if (numCodingQuestions > 0 && codingQuestions.length !== numCodingQuestions) {
100629
- throw new Error(`Expected ${numCodingQuestions} coding questions, but got ${codingQuestions.length}`);
100630
- }
100631
- const diversityAnalysis = validateConsecutiveTypes(aiGeneratedContent.quizPlan);
100632
- logger.log("VALIDATION_COMPLETE", {
100633
- questionCount: aiGeneratedContent.quizPlan.length,
100634
- codingQuestionCount: codingQuestions.length,
100635
- maxConsecutiveType: diversityAnalysis.maxConsecutive,
100636
- questionTypeDistribution: aiGeneratedContent.diversityMetrics?.questionTypeDistribution || {}
100637
- }, Date.now() - validationStartTime);
100638
- const finalResult = {
100639
- ...aiGeneratedContent,
100640
- logs: logger.getLogs()
100641
- };
100642
- logger.log("GENERATION_COMPLETE", {
100643
- totalDuration: logger.getTotalDuration(),
100644
- success: true,
100645
- finalQuestionCount: finalResult.quizPlan.length
100646
- }, logger.getTotalDuration());
100647
- console.log("\n=== QUIZ PLAN GENERATION SUMMARY ===");
100648
- console.log(`\u2705 Successfully generated ${finalResult.quizPlan.length} questions`);
100649
- console.log(`\u23F1\uFE0F Total generation time: ${logger.getTotalDuration()}ms`);
100650
- console.log(`\u{1F3AF} Question types: ${Object.keys(finalResult.diversityMetrics?.questionTypeDistribution || {}).join(", ")}`);
100651
- console.log(`\u{1F9E0} Bloom levels: ${Object.keys(finalResult.diversityMetrics?.bloomLevelDistribution || {}).join(", ")}`);
100652
- if (numCodingQuestions > 0) {
100653
- console.log(`\u{1F4BB} Coding questions: ${codingQuestions.length}/${numCodingQuestions} required`);
100654
- }
100655
- console.log(JSON.stringify(finalResult));
100656
- console.log("=====================================\n");
100657
- return finalResult;
100658
- } catch (error) {
100659
- logger.log("ERROR", {
100660
- message: error.message,
100661
- stack: error.stack,
100662
- totalDuration: logger.getTotalDuration()
100663
- });
100664
- console.error("\u274C Quiz Plan Generation Failed:", error.message);
100665
- console.error("\u{1F4CB} Full logs available in returned object");
100666
- throw new Error(`Failed to generate Quiz Plan: ${error.message}`);
100667
- }
100668
- }
100669
- function validateConsecutiveTypes(quizPlan) {
100670
- let maxConsecutive = 1;
100671
- let maxType = quizPlan[0]?.plannedQuestionType || "";
100672
- let maxStartIndex = 0;
100673
- let currentConsecutive = 1;
100674
- let currentType = quizPlan[0]?.plannedQuestionType || "";
100675
- let currentStartIndex = 0;
100676
- for (let i2 = 1; i2 < quizPlan.length; i2++) {
100677
- if (quizPlan[i2].plannedQuestionType === currentType) {
100678
- currentConsecutive++;
100679
- } else {
100680
- if (currentConsecutive > maxConsecutive) {
100681
- maxConsecutive = currentConsecutive;
100682
- maxType = currentType;
100683
- maxStartIndex = currentStartIndex;
100684
- }
100685
- currentConsecutive = 1;
100686
- currentType = quizPlan[i2].plannedQuestionType;
100687
- currentStartIndex = i2;
100688
- }
100689
- }
100690
- if (currentConsecutive > maxConsecutive) {
100691
- maxConsecutive = currentConsecutive;
100692
- maxType = currentType;
100693
- maxStartIndex = currentStartIndex;
100694
- }
100695
- return {
100696
- maxConsecutive,
100697
- type: maxType,
100698
- startIndex: maxStartIndex
100699
- };
100700
- }
100701
-
100702
- // src/ai/flows/generate-questions-from-quiz-plan.ts
100703
- init_react_shim();
100704
-
100705
- // src/services/TopicDataService.ts
100706
- init_react_shim();
100707
- var TopicDataService = class {
100708
- /**
100709
- * Saves an array of LearningObjective objects to Local Storage, overwriting existing data.
100710
- * @param data The array of learning objectives to save.
100711
- */
100712
- static saveData(data) {
100713
- try {
100714
- if (typeof window === "undefined") return;
100715
- const serializedData = JSON.stringify(data);
100716
- localStorage.setItem(this.STORAGE_KEY, serializedData);
100717
- } catch (error) {
100718
- console.error("Error saving learning objectives to Local Storage:", error);
100719
- }
100720
- }
100721
- /**
100722
- * Merges a new set of learning objectives with the existing data in Local Storage.
100723
- * If an LO ID from newData already exists, it will be updated. Otherwise, it will be added.
100724
- * @param newData The array of new or updated learning objectives.
100725
- */
100726
- static mergeData(newData) {
100727
- const existingData = this.getData();
100728
- const loMap = new Map(existingData.map((lo) => [lo.loId, lo]));
100729
- newData.forEach((newLo) => {
100730
- loMap.set(newLo.loId, newLo);
100731
- });
100732
- const mergedData = Array.from(loMap.values());
100733
- this.saveData(mergedData);
100734
- }
100735
- /**
100736
- * Retrieves the array of LearningObjective objects from Local Storage.
100737
- * @returns An array of learning objectives, or an empty array if none are found or an error occurs.
100738
- */
100739
- static getData() {
100740
- try {
100741
- if (typeof window === "undefined") return [];
100742
- const storedData = localStorage.getItem(this.STORAGE_KEY);
100743
- return storedData ? JSON.parse(storedData) : [];
100744
- } catch (error) {
100745
- console.error("Error retrieving learning objectives from Local Storage:", error);
100746
- this.clearData();
100747
- return [];
100748
- }
100749
- }
100750
- /**
100751
- * Removes all learning objective data from Local Storage.
100752
- */
100753
- static clearData() {
100754
- try {
100755
- if (typeof window === "undefined") return;
100756
- localStorage.removeItem(this.STORAGE_KEY);
100757
- } catch (error) {
100758
- console.error("Error clearing learning objectives from Local Storage:", error);
100759
- }
100760
- }
100761
- /**
100762
- * Parses TSV content into an array of LearningObjective objects.
100763
- * @param tsvContent The raw string content from a .tsv file.
100764
- * @returns An object containing the successfully parsed data and any errors encountered.
100765
- */
100766
- static parseTSV(tsvContent) {
100767
- const lines = tsvContent.split("\n").filter((line) => line.trim() !== "");
100768
- if (lines.length < 2) {
100769
- return { data: [], errors: ["File is empty or contains only a header."] };
100770
- }
100771
- const headerLine = lines.shift();
100772
- const headers = headerLine.split(" ").map((h3) => h3.trim());
100773
- if (headers.length !== this.EXPECTED_HEADERS.length || !this.EXPECTED_HEADERS.every((h3, i2) => h3 === headers[i2])) {
100774
- const errorMsg = `Invalid TSV header. Expected: "${this.EXPECTED_HEADERS.join(" ")}". Received: "${headers.join(" ")}"`;
100775
- return { data: [], errors: [errorMsg] };
100776
- }
100777
- const data = [];
100778
- const errors2 = [];
100779
- lines.forEach((line, index3) => {
100780
- const values = line.split(" ").map((v) => v.trim());
100781
- if (values.length !== this.EXPECTED_HEADERS.length) {
100782
- errors2.push(`Line ${index3 + 2}: Incorrect number of columns. Expected ${this.EXPECTED_HEADERS.length}, but got ${values.length}.`);
100783
- return;
100784
- }
100785
- const [
100786
- loId,
100787
- loDescription,
100788
- subject,
100789
- category,
100790
- topic,
100791
- keywordsStr,
100792
- grade,
100793
- stemElementsStr,
100794
- bloomLevelsStr
100795
- ] = values;
100796
- if (!loId || !subject || !category || !topic) {
100797
- errors2.push(`Line ${index3 + 2}: Missing required fields (LO ID, Subject, Category, or Topic).`);
100798
- return;
100799
- }
100800
- const learningObjective = {
100801
- loId,
100802
- loDescription,
100803
- subject,
100804
- category,
100805
- topic,
100806
- keywords: keywordsStr.split(",").map((k3) => k3.trim()).filter(Boolean),
100807
- grade,
100808
- stemElements: stemElementsStr.split(",").map((s4) => s4.trim()).filter(Boolean),
100809
- bloomLevelsGuideline: bloomLevelsStr.split(",").map((b2) => b2.trim()).filter(Boolean)
100810
- };
100811
- data.push(learningObjective);
100812
- });
100813
- return { data, errors: errors2 };
100814
- }
100815
- /**
100816
- * Gets a unique list of all subjects from the stored data.
100817
- * @returns An array of subject strings.
100818
- */
100819
- static getSubjects() {
100820
- const data = this.getData();
100821
- const subjects = data.map((item) => item.subject);
100822
- return [...new Set(subjects)].sort();
100823
- }
100824
- /**
100825
- * Gets a unique list of categories for a given subject.
100826
- * @param subject The subject to filter by.
100827
- * @returns An array of category strings.
100828
- */
100829
- static getCategoriesBySubject(subject) {
100830
- const data = this.getData();
100831
- const categories = data.filter((item) => item.subject === subject).map((item) => item.category);
100832
- return [...new Set(categories)].sort();
100833
- }
100834
- /**
100835
- * Gets a unique list of topics for a given category.
100836
- * @param category The category to filter by.
100837
- * @returns An array of topic strings.
100838
- */
100839
- static getTopicsByCategory(category) {
100840
- const data = this.getData();
100841
- const topics = data.filter((item) => item.category === category).map((item) => item.topic);
100842
- return [...new Set(topics)].sort();
100843
- }
100844
- /**
100845
- * Retrieves all LearningObjective details for a given list of topics.
100846
- * @param topics An array of topic strings to search for.
100847
- * @returns An array of matching LearningObjective objects.
100848
- */
100849
- static getLearningObjectivesByTopics(topics) {
100850
- const data = this.getData();
100851
- const topicSet = new Set(topics);
100852
- return data.filter((item) => topicSet.has(item.topic));
100853
- }
100854
- };
100855
- TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
100856
- TopicDataService.EXPECTED_HEADERS = [
100857
- "LO ID",
100858
- "LO Description",
100859
- "Subject",
100860
- "Category",
100861
- "Topic",
100862
- "Keywords",
100863
- "Grade",
100864
- "STEM Element(s)",
100865
- "Bloom\u2019s Level(s) Guideline"
100866
- ];
100867
-
100868
100068
  // src/ai/flows/question-gen/generate-mrq-question.ts
100869
100069
  init_react_shim();
100870
100070
 
@@ -100881,7 +100081,7 @@ var AIMRQOutputFieldsSchema = z.object({
100881
100081
  correctTempOptionIds: z.array(z.string()).min(1),
100882
100082
  explanation: z.string().optional(),
100883
100083
  points: z.number().optional().default(10),
100884
- difficulty: z.enum(["easy", "medium", "hard"]).optional(),
100084
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
100885
100085
  topic: z.string().optional(),
100886
100086
  verifiedCategory: z.string().optional().describe("The category this question actually addresses.")
100887
100087
  });
@@ -100900,7 +100100,7 @@ Previous attempts failed due to validation errors. Pay close attention to the nu
100900
100100
  const imageContextInstruction = imageUrl ? `**Image Context:** You MUST analyze the provided image. The question and options must be directly related to the content of this image.` : "";
100901
100101
  const contextStrings = [
100902
100102
  `**Required Category:** ${category} (This is the ONLY language to be used)`,
100903
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
100103
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
100904
100104
  imageContextInstruction,
100905
100105
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
100906
100106
  quizContext?.targetMisconception && `**Target Misconception:** Use this to create plausible incorrect answers (distractors). The misconception is: "${quizContext.targetMisconception}"`,
@@ -100918,7 +100118,7 @@ Previous attempts failed due to validation errors. Pay close attention to the nu
100918
100118
  correctTempOptionIds: ["A", "C", "D"],
100919
100119
  explanation: "Object-Oriented, Functional, and Procedural are all major programming paradigms. Assembly is a low-level language, and Middleware is a type of software, not a paradigm.",
100920
100120
  points: 10,
100921
- difficulty: "medium",
100121
+ difficulty: "Medium",
100922
100122
  topic: "Programming Paradigms",
100923
100123
  verifiedCategory: category
100924
100124
  }, null, 2);
@@ -101082,7 +100282,7 @@ var AIShortAnswerOutputFieldsSchema = z.object({
101082
100282
  // isCaseSensitive không cần thiết ở đây, chúng ta sẽ quản lý nó ở phía client
101083
100283
  explanation: z.string().optional(),
101084
100284
  points: z.number().optional().default(10),
101085
- difficulty: z.enum(["easy", "medium", "hard"]).optional(),
100285
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
101086
100286
  topic: z.string().optional(),
101087
100287
  verifiedCategory: z.string().optional()
101088
100288
  // Thêm để xác thực
@@ -101102,7 +100302,7 @@ Previous attempts failed. Ensure 'acceptedAnswers' is a non-empty array of strin
101102
100302
  const imageContextInstruction = imageUrl ? `**Image Context:** You MUST analyze the provided image. The question and its short answer must be directly related to the content of this image.` : "";
101103
100303
  const contextStrings = [
101104
100304
  `**Required Category:** ${category}`,
101105
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
100305
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
101106
100306
  imageContextInstruction,
101107
100307
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
101108
100308
  quizContext?.targetMisconception && `**Target Misconception:** The question should require an answer that corrects this specific misconception: "${quizContext.targetMisconception}"`
@@ -101112,7 +100312,7 @@ Previous attempts failed. Ensure 'acceptedAnswers' is a non-empty array of strin
101112
100312
  acceptedAnswers: ["let"],
101113
100313
  explanation: "The 'let' keyword is used to declare constants, which are values that cannot be changed after they are set. 'var' is used for variables.",
101114
100314
  points: 10,
101115
- difficulty: "easy",
100315
+ difficulty: "Easy",
101116
100316
  topic: "Swift Constants",
101117
100317
  verifiedCategory: category
101118
100318
  }, null, 2);
@@ -101245,7 +100445,7 @@ var AINumericOutputFieldsSchema = z.object({
101245
100445
  tolerance: z.number().min(0).optional(),
101246
100446
  explanation: z.string().optional(),
101247
100447
  points: z.number().optional().default(10),
101248
- difficulty: z.enum(["easy", "medium", "hard"]).optional(),
100448
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
101249
100449
  topic: z.string().optional(),
101250
100450
  verifiedCategory: z.string().optional()
101251
100451
  // Thêm để xác thực
@@ -101265,7 +100465,7 @@ Previous attempts failed. Ensure the 'answer' is a valid number and fits within
101265
100465
  const imageContextInstruction = imageUrl ? `**Image Context:** You MUST analyze the provided image. The question and its numerical answer must be directly related to the content of this image.` : "";
101266
100466
  const contextStrings = [
101267
100467
  `**Required Category:** ${category}`,
101268
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
100468
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
101269
100469
  imageContextInstruction,
101270
100470
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
101271
100471
  quizContext?.targetMisconception && `**Target Misconception:** The question should clarify this numerical error: "${quizContext.targetMisconception}"`
@@ -101281,7 +100481,7 @@ Previous attempts failed. Ensure the 'answer' is a valid number and fits within
101281
100481
  tolerance: 0,
101282
100482
  explanation: "An Int8 uses 8 bits. One bit is for the sign, leaving 7 bits for the value. The range is from -128 to 127 (2^7 - 1).",
101283
100483
  points: 10,
101284
- difficulty: "medium",
100484
+ difficulty: "Medium",
101285
100485
  topic: "Swift Data Types",
101286
100486
  verifiedCategory: category
101287
100487
  }, null, 2);
@@ -101431,7 +100631,7 @@ var AIFillInTheBlanksOutputFieldsSchema = z.object({
101431
100631
  })).min(1).describe("An array of text and blank segments representing the question."),
101432
100632
  explanation: z.string().optional(),
101433
100633
  points: z.number().optional().default(10),
101434
- difficulty: z.enum(["easy", "medium", "hard"]).optional(),
100634
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
101435
100635
  topic: z.string().optional(),
101436
100636
  verifiedCategory: z.string().optional()
101437
100637
  // Thêm để xác thực
@@ -101451,7 +100651,7 @@ Previous attempts failed. Pay strict attention to the JSON schema, especially th
101451
100651
  const imageContextInstruction = imageUrl ? `**Image Context:** You MUST analyze the provided image. The question and blanks must be directly related to the content of this image.` : "";
101452
100652
  const contextStrings = [
101453
100653
  `**Required Category:** ${category}`,
101454
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
100654
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
101455
100655
  imageContextInstruction,
101456
100656
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
101457
100657
  quizContext?.targetMisconception && `**Target Misconception:** Design the blank to test this specific point: "${quizContext.targetMisconception}"`
@@ -101465,7 +100665,7 @@ Previous attempts failed. Pay strict attention to the JSON schema, especially th
101465
100665
  ],
101466
100666
  explanation: "The 'func' keyword is used to declare a function in the Swift programming language.",
101467
100667
  points: 10,
101468
- difficulty: "easy",
100668
+ difficulty: "Easy",
101469
100669
  topic: "Swift Function Declaration",
101470
100670
  verifiedCategory: category
101471
100671
  }, null, 2);
@@ -101619,7 +100819,7 @@ var AISequenceOutputFieldsSchema = z.object({
101619
100819
  itemsInCorrectOrder: z.array(z.string().min(1)).min(2).describe("An array of strings, with each string representing an item to be sequenced. The array itself MUST be in the correct final order."),
101620
100820
  explanation: z.string().optional(),
101621
100821
  points: z.number().optional().default(10),
101622
- difficulty: z.enum(["easy", "medium", "hard"]).optional(),
100822
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
101623
100823
  topic: z.string().optional(),
101624
100824
  verifiedCategory: z.string().optional()
101625
100825
  // Thêm để xác thực
@@ -101639,7 +100839,7 @@ Previous attempts failed. Ensure the 'itemsInCorrectOrder' array has exactly the
101639
100839
  const imageContextInstruction = imageUrl ? `**Image Context:** You MUST analyze the provided image. The sequence of items must be directly related to the process or content shown in this image.` : "";
101640
100840
  const contextStrings = [
101641
100841
  `**Required Category:** ${category}`,
101642
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
100842
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
101643
100843
  imageContextInstruction,
101644
100844
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
101645
100845
  quizContext?.targetMisconception && `**Target Misconception:** The sequence should clarify this specific process error: "${quizContext.targetMisconception}"`
@@ -101654,7 +100854,7 @@ Previous attempts failed. Ensure the 'itemsInCorrectOrder' array has exactly the
101654
100854
  ],
101655
100855
  explanation: "This is the fundamental sequence for a basic data task in URLSession.",
101656
100856
  points: 10,
101657
- difficulty: "medium",
100857
+ difficulty: "Medium",
101658
100858
  topic: "Swift Networking",
101659
100859
  verifiedCategory: category
101660
100860
  }, null, 2);
@@ -101796,7 +100996,7 @@ var AIMatchingOutputFieldsSchema = z.object({
101796
100996
  })).min(2),
101797
100997
  explanation: z.string().optional(),
101798
100998
  points: z.number().optional().default(10),
101799
- difficulty: z.enum(["easy", "medium", "hard"]).optional(),
100999
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
101800
101000
  topic: z.string().optional(),
101801
101001
  verifiedCategory: z.string().optional()
101802
101002
  // Thêm để xác thực
@@ -101816,7 +101016,7 @@ Previous attempts failed. Please ensure the 'correctPairs' array has exactly the
101816
101016
  const imageContextInstruction = imageUrl ? `**Image Context:** You MUST analyze the provided image. The matching pairs must be directly related to the content of this image.` : "";
101817
101017
  const contextStrings = [
101818
101018
  `**Required Category:** ${category}`,
101819
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
101019
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
101820
101020
  imageContextInstruction,
101821
101021
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
101822
101022
  quizContext?.targetMisconception && `**Target Misconception:** Design a pair that specifically tests this confusion: "${quizContext.targetMisconception}"`
@@ -101830,7 +101030,7 @@ Previous attempts failed. Please ensure the 'correctPairs' array has exactly the
101830
101030
  ],
101831
101031
  explanation: "These are the fundamental characteristics of Swift's main collection types.",
101832
101032
  points: 10,
101833
- difficulty: "easy",
101033
+ difficulty: "Easy",
101834
101034
  topic: "Swift Collection Types",
101835
101035
  verifiedCategory: category
101836
101036
  }, null, 2);
@@ -101960,6 +101160,821 @@ async function generateMatchingQuestion(clientInput, apiKey) {
101960
101160
  return { error: errorMessage };
101961
101161
  }
101962
101162
 
101163
+ // src/react-ui/components/authoring/AIQuestionGeneratorModal.tsx
101164
+ var supportedQuestionTypesForAI = [
101165
+ { value: "true_false", label: "True/False" },
101166
+ { value: "multiple_choice", label: "Multiple Choice" },
101167
+ { value: "multiple_response", label: "Multiple Response" },
101168
+ { value: "short_answer", label: "Short Answer" },
101169
+ { value: "numeric", label: "Numeric" },
101170
+ { value: "fill_in_the_blanks", label: "Fill In The Blanks" },
101171
+ { value: "sequence", label: "Sequence" },
101172
+ { value: "matching", label: "Matching" }
101173
+ ];
101174
+ var AIQuestionGeneratorModal = ({
101175
+ isOpen,
101176
+ onClose,
101177
+ onQuestionGenerated,
101178
+ language: language3,
101179
+ questionType: questionTypeProp,
101180
+ subjects = [],
101181
+ topics = [],
101182
+ gradeLevels = [],
101183
+ bloomLevels = []
101184
+ }) => {
101185
+ const [prompt, setPrompt] = useState("");
101186
+ const [isLoading, setIsLoading] = useState(false);
101187
+ const [error, setError] = useState(null);
101188
+ const { toast: toast2 } = useToast();
101189
+ const [subjectCode, setSubjectCode] = useState("");
101190
+ const [topicCode, setTopicCode] = useState("");
101191
+ const [gradeBand, setGradeBand] = useState("");
101192
+ const [bloomLevelCode, setBloomLevelCode] = useState("");
101193
+ const [selectedQuestionType, setSelectedQuestionType] = useState("multiple_choice");
101194
+ const [numberOfOptions, setNumberOfOptions] = useState(4);
101195
+ const [minCorrectAnswers, setMinCorrectAnswers] = useState(2);
101196
+ const [maxCorrectAnswers, setMaxCorrectAnswers] = useState(3);
101197
+ const [isCaseSensitive, setIsCaseSensitive] = useState(false);
101198
+ const [numberOfBlanks, setNumberOfBlanks] = useState(2);
101199
+ const [numberOfSequenceItems, setNumberOfSequenceItems] = useState(4);
101200
+ const [numberOfMatchingPairs, setNumberOfMatchingPairs] = useState(4);
101201
+ const [isApiKeyManagerModalOpen, setIsApiKeyManagerModalOpen] = useState(false);
101202
+ const [geminiApiKeyExists, setGeminiApiKeyExists] = useState(false);
101203
+ const finalQuestionType = questionTypeProp || selectedQuestionType;
101204
+ useEffect(() => {
101205
+ if (isOpen) {
101206
+ setPrompt("");
101207
+ setError(null);
101208
+ setIsLoading(false);
101209
+ setSubjectCode("");
101210
+ setTopicCode("");
101211
+ setGradeBand("");
101212
+ setBloomLevelCode("");
101213
+ setSelectedQuestionType(questionTypeProp || "multiple_choice");
101214
+ setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
101215
+ }
101216
+ }, [isOpen, questionTypeProp]);
101217
+ const filteredTopics = useMemo(() => {
101218
+ if (!subjectCode) return [];
101219
+ return topics.filter((t4) => t4.subjectCode === subjectCode);
101220
+ }, [subjectCode, topics]);
101221
+ const handleApiKeyModalClose = () => {
101222
+ setIsApiKeyManagerModalOpen(false);
101223
+ setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
101224
+ };
101225
+ const handleSubmit = async () => {
101226
+ if (!prompt.trim()) {
101227
+ setError("Please provide a prompt for the question.");
101228
+ return;
101229
+ }
101230
+ const geminiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
101231
+ if (!geminiKey) {
101232
+ setError("Gemini API Key is not set. Please configure it.");
101233
+ setGeminiApiKeyExists(false);
101234
+ return;
101235
+ }
101236
+ setGeminiApiKeyExists(true);
101237
+ setError(null);
101238
+ setIsLoading(true);
101239
+ try {
101240
+ const quizContext = {
101241
+ plannedTopic: prompt,
101242
+ originalSubject: subjectCode,
101243
+ originalTopic: topicCode,
101244
+ gradeBand,
101245
+ plannedBloomLevel: bloomLevelCode
101246
+ };
101247
+ const baseClientInput = {
101248
+ language: language3,
101249
+ difficulty: "Medium",
101250
+ quizContext
101251
+ };
101252
+ let generatedResult = {};
101253
+ switch (finalQuestionType) {
101254
+ case "true_false":
101255
+ generatedResult = await generateTrueFalseQuestion(baseClientInput, geminiKey);
101256
+ break;
101257
+ case "multiple_choice":
101258
+ generatedResult = await generateMCQQuestion({ ...baseClientInput, numberOfOptions }, geminiKey);
101259
+ break;
101260
+ case "multiple_response":
101261
+ generatedResult = await generateMRQQuestion({ ...baseClientInput, numberOfOptions, minCorrectAnswers, maxCorrectAnswers }, geminiKey);
101262
+ break;
101263
+ case "short_answer":
101264
+ generatedResult = await generateShortAnswerQuestion({ ...baseClientInput, isCaseSensitive }, geminiKey);
101265
+ break;
101266
+ case "numeric":
101267
+ generatedResult = await generateNumericQuestion({ ...baseClientInput, allowDecimals: true, tolerance: 0 }, geminiKey);
101268
+ break;
101269
+ case "fill_in_the_blanks":
101270
+ generatedResult = await generateFillInTheBlanksQuestion({ ...baseClientInput, numberOfBlanks, isCaseSensitive }, geminiKey);
101271
+ break;
101272
+ case "sequence":
101273
+ generatedResult = await generateSequenceQuestion({ ...baseClientInput, numberOfItems: numberOfSequenceItems }, geminiKey);
101274
+ break;
101275
+ case "matching":
101276
+ generatedResult = await generateMatchingQuestion({ ...baseClientInput, numberOfPairs: numberOfMatchingPairs, shuffleOptions: true }, geminiKey);
101277
+ break;
101278
+ default:
101279
+ throw new Error(`AI generation for '${finalQuestionType}' is not implemented in this flow.`);
101280
+ }
101281
+ if (generatedResult.error) {
101282
+ throw new Error(generatedResult.error);
101283
+ }
101284
+ if (generatedResult.question) {
101285
+ const completeQuestion = generatedResult.question;
101286
+ completeQuestion.subject = subjectCode;
101287
+ completeQuestion.topic = topicCode;
101288
+ completeQuestion.gradeBand = gradeBand;
101289
+ completeQuestion.bloomLevel = bloomLevelCode;
101290
+ if (completeQuestion.points === void 0) completeQuestion.points = 10;
101291
+ onQuestionGenerated(completeQuestion);
101292
+ toast2({ title: "AI Question Generated", description: "Review and edit the generated question." });
101293
+ onClose();
101294
+ } else {
101295
+ throw new Error("AI did not return a valid question object.");
101296
+ }
101297
+ } catch (e3) {
101298
+ console.error("AI Question Generation Error:", e3);
101299
+ let errorMessage = e3.message || "An unknown error occurred.";
101300
+ if (typeof errorMessage === "string" && errorMessage.includes("API key not valid")) {
101301
+ errorMessage = "The provided Google Gemini API Key is invalid. Please check it.";
101302
+ setGeminiApiKeyExists(false);
101303
+ }
101304
+ setError(errorMessage);
101305
+ toast2({ title: "AI Generation Failed", description: errorMessage, variant: "destructive" });
101306
+ } finally {
101307
+ setIsLoading(false);
101308
+ }
101309
+ };
101310
+ const comboboxOptions = {
101311
+ subjects: subjects.map((s4) => ({ value: s4.code, label: s4.name })),
101312
+ topics: filteredTopics.map((t4) => ({ value: t4.code, label: t4.name })),
101313
+ gradeLevels: gradeLevels.map((g) => ({ value: g.code, label: g.name })),
101314
+ bloomLevels: bloomLevels.map((b2) => ({ value: b2.code, label: b2.name }))
101315
+ };
101316
+ const renderSpecificParams = () => {
101317
+ switch (finalQuestionType) {
101318
+ case "multiple_choice":
101319
+ case "multiple_response":
101320
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "ai-num-options" }, "Number of Options (2-8)"), /* @__PURE__ */ React169__default.createElement(
101321
+ Input,
101322
+ {
101323
+ id: "ai-num-options",
101324
+ type: "number",
101325
+ value: numberOfOptions,
101326
+ onChange: (e3) => setNumberOfOptions(parseInt(e3.target.value, 10)),
101327
+ min: 2,
101328
+ max: 8
101329
+ }
101330
+ ), finalQuestionType === "multiple_response" && /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "ai-min-correct" }, "Min Correct Answers"), /* @__PURE__ */ React169__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__default.createElement(Label2, { htmlFor: "ai-max-correct" }, "Max Correct Answers"), /* @__PURE__ */ React169__default.createElement(Input, { id: "ai-max-correct", type: "number", value: maxCorrectAnswers, onChange: (e3) => setMaxCorrectAnswers(parseInt(e3.target.value, 10)), min: minCorrectAnswers, max: numberOfOptions })));
101331
+ case "short_answer":
101332
+ case "fill_in_the_blanks":
101333
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-2 pt-4 border-t" }, /* @__PURE__ */ React169__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__default.createElement(Label2, { htmlFor: "ai-case-sensitive" }, "Case Sensitive Answers"), finalQuestionType === "fill_in_the_blanks" && /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1 ml-4" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "ai-num-blanks" }, "Number of Blanks (1-5)"), /* @__PURE__ */ React169__default.createElement(Input, { id: "ai-num-blanks", type: "number", value: numberOfBlanks, onChange: (e3) => setNumberOfBlanks(parseInt(e3.target.value, 10)), min: 1, max: 5 })));
101334
+ case "sequence":
101335
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "ai-num-seq-items" }, "Number of Items to Sequence (2-10)"), /* @__PURE__ */ React169__default.createElement(Input, { id: "ai-num-seq-items", type: "number", value: numberOfSequenceItems, onChange: (e3) => setNumberOfSequenceItems(parseInt(e3.target.value, 10)), min: 2, max: 10 }));
101336
+ case "matching":
101337
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "ai-num-match-pairs" }, "Number of Pairs to Match (2-8)"), /* @__PURE__ */ React169__default.createElement(Input, { id: "ai-num-match-pairs", type: "number", value: numberOfMatchingPairs, onChange: (e3) => setNumberOfMatchingPairs(parseInt(e3.target.value, 10)), min: 2, max: 8 }));
101338
+ default:
101339
+ return null;
101340
+ }
101341
+ };
101342
+ return /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => {
101343
+ if (!open) onClose();
101344
+ } }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-[600px]" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, { className: "flex items-center font-headline text-2xl" }, /* @__PURE__ */ React169__default.createElement(WandSparkles, { className: "mr-2 h-6 w-6 text-primary" }), " AI Question Generator"), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, "Provide a prompt and metadata to generate a '", finalQuestionType, "' question.")), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 py-4 max-h-[60vh] overflow-y-auto px-2" }, !geminiApiKeyExists && /* @__PURE__ */ React169__default.createElement("div", { className: "p-3 mb-4 border border-amber-500 bg-amber-50 rounded-md text-amber-700" }), !questionTypeProp && /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "ai-q-type-select" }, "Question Type"), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedQuestionType, onValueChange: (v) => setSelectedQuestionType(v) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "ai-q-type-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, supportedQuestionTypesForAI.map((type) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: type.value, value: type.value }, type.label))))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "ai-prompt" }, "Prompt / Topic"), /* @__PURE__ */ React169__default.createElement(
101345
+ Textarea,
101346
+ {
101347
+ id: "ai-prompt",
101348
+ value: prompt,
101349
+ onChange: (e3) => setPrompt(e3.target.value),
101350
+ placeholder: "e.g., The process of photosynthesis, The causes of World War II, Basic Algebra Equations",
101351
+ className: "min-h-[100px]"
101352
+ }
101353
+ )), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, null, "Subject"), /* @__PURE__ */ React169__default.createElement(EditableCombobox, { options: comboboxOptions.subjects, value: subjectCode, onChange: setSubjectCode, placeholder: "Select or type a Subject..." })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, null, "Topic"), /* @__PURE__ */ React169__default.createElement(EditableCombobox, { options: comboboxOptions.topics, value: topicCode, onChange: setTopicCode, placeholder: "Select or type a Topic...", disabled: !subjectCode })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, null, "Grade Level"), /* @__PURE__ */ React169__default.createElement(EditableCombobox, { options: comboboxOptions.gradeLevels, value: gradeBand, onChange: setGradeBand, placeholder: "Select or type a Grade..." })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, null, "Bloom's Level"), /* @__PURE__ */ React169__default.createElement(EditableCombobox, { options: comboboxOptions.bloomLevels, value: bloomLevelCode, onChange: setBloomLevelCode, placeholder: "Select a Bloom's Level..." }))), renderSpecificParams(), error && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error)), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline" }, "Cancel")), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", onClick: handleSubmit, disabled: isLoading || !prompt.trim() || !geminiApiKeyExists }, isLoading ? /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), "Generate Question")))), /* @__PURE__ */ React169__default.createElement(APIKeyManagerModal, { isOpen: isApiKeyManagerModalOpen, onClose: handleApiKeyModalClose }));
101354
+ };
101355
+
101356
+ // src/react-ui/components/authoring/AIFullQuizGeneratorModal.tsx
101357
+ init_react_shim();
101358
+
101359
+ // src/ai/flows/generate-quiz-plan.ts
101360
+ init_react_shim();
101361
+
101362
+ // src/ai/flows/generate-quiz-plan-types.ts
101363
+ init_react_shim();
101364
+ var TopicWithMetadataSchema = z.object({
101365
+ topic: z.string().min(1),
101366
+ ratio: z.number().min(0).max(100),
101367
+ originalLoId: z.string().optional(),
101368
+ originalSubject: z.string().optional(),
101369
+ originalCategory: z.string().optional(),
101370
+ originalTopic: z.string().optional(),
101371
+ commonMisconceptions: z.array(z.string()).optional()
101372
+ });
101373
+ var BloomLevelStringsEnum = z.enum(["remembering", "understanding", "applying", "analyzing", "evaluating", "creating"]);
101374
+ var fullQuizSupportedQuestionTypesArray = [
101375
+ "true_false",
101376
+ "multiple_choice",
101377
+ "multiple_response",
101378
+ "short_answer",
101379
+ "numeric",
101380
+ "fill_in_the_blanks",
101381
+ "sequence",
101382
+ "matching",
101383
+ "drag_and_drop",
101384
+ "coding"
101385
+ ];
101386
+ z.object({
101387
+ language: z.string().optional().default("English"),
101388
+ totalQuestions: z.number().int().min(1).max(50),
101389
+ numCodingQuestions: z.number().optional().default(0),
101390
+ topics: z.array(TopicWithMetadataSchema).min(1),
101391
+ bloomLevels: z.array(z.object({
101392
+ level: BloomLevelStringsEnum,
101393
+ ratio: z.number().min(0).max(100)
101394
+ })).min(1),
101395
+ selectedContextIds: z.array(z.string()).optional(),
101396
+ selectedQuestionTypes: z.array(z.enum(fullQuizSupportedQuestionTypesArray)).min(1),
101397
+ imageContexts: z.array(z.custom()).optional().describe("Library of available image contexts for the AI to use.")
101398
+ });
101399
+ var PlannedQuestionSchema = z.object({
101400
+ plannedTopic: z.string().min(1).describe("The specific, assessable topic for this question."),
101401
+ plannedQuestionType: z.enum(fullQuizSupportedQuestionTypesArray).describe("The specific question type chosen."),
101402
+ plannedBloomLevel: BloomLevelStringsEnum.describe("The Bloom's level assigned."),
101403
+ plannedContextId: z.string().optional().describe("The specific context ID chosen for this question."),
101404
+ imageId: z.string().nullable().optional().describe("The ID of the image from the context library to be used for this question."),
101405
+ targetMisconception: z.string().optional().describe("A specific common misconception this question should target."),
101406
+ difficultyReason: z.string().optional().describe("Strategic explanation of difficulty choice and placement."),
101407
+ topicSpecificity: z.enum(["broad", "focused", "specific"]).optional().describe("How specific the topic coverage should be."),
101408
+ originalLoId: z.string().optional(),
101409
+ originalSubject: z.string().optional(),
101410
+ originalCategory: z.string().optional(),
101411
+ originalTopic: z.string().optional()
101412
+ });
101413
+ var GenerateQuizPlanOutputSchema = z.object({
101414
+ quizPlan: z.array(PlannedQuestionSchema).describe("A detailed plan for each question in the quiz."),
101415
+ diversityMetrics: z.object({
101416
+ questionTypeDistribution: z.record(z.number()).optional(),
101417
+ bloomLevelDistribution: z.record(z.number()).optional(),
101418
+ maxConsecutiveSameType: z.number().optional()
101419
+ }).optional().describe("Metrics showing the diversity achieved in the plan."),
101420
+ planningStrategy: z.object({
101421
+ overallApproach: z.string().optional(),
101422
+ keyDecisions: z.array(z.string()).optional()
101423
+ }).optional()
101424
+ });
101425
+
101426
+ // src/ai/flows/generate-quiz-plan.ts
101427
+ var QuizPlanLogger = class {
101428
+ constructor() {
101429
+ this.logs = [];
101430
+ this.startTime = Date.now();
101431
+ }
101432
+ log(phase, data, duration) {
101433
+ this.logs.push({
101434
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
101435
+ phase,
101436
+ data,
101437
+ duration
101438
+ });
101439
+ if (duration !== void 0) {
101440
+ console.log(`[${phase}] Completed in ${duration}ms:`, data);
101441
+ } else {
101442
+ console.log(`[${phase}]:`, data);
101443
+ }
101444
+ }
101445
+ getLogs() {
101446
+ return this.logs;
101447
+ }
101448
+ getTotalDuration() {
101449
+ return Date.now() - this.startTime;
101450
+ }
101451
+ };
101452
+ function generateQuestionTypeSelectionGuidance() {
101453
+ return `
101454
+ QUESTION TYPE SELECTION BEST PRACTICES:
101455
+
101456
+ **TRUE_FALSE (true_false)**
101457
+ - Best for: Binary concepts, fact verification, common misconceptions
101458
+ - Bloom levels: Primarily Remembering, Understanding
101459
+ - Use when: Testing definitive statements, clarifying misconceptions
101460
+ - Example: "Photosynthesis only occurs during daytime" (targets timing misconception)
101461
+
101462
+ **MULTIPLE CHOICE (multiple_choice)**
101463
+ - Best for: Concept selection, process understanding, comparison
101464
+ - Bloom levels: All levels, especially Understanding and Applying
101465
+ - Use when: Testing conceptual understanding with clear alternatives
101466
+ - Example: "Which factor most affects enzyme activity?" (applying knowledge)
101467
+
101468
+ **MULTIPLE RESPONSE (multiple_response)**
101469
+ - Best for: Identifying multiple correct factors, comprehensive understanding
101470
+ - Bloom levels: Understanding, Analyzing, Evaluating
101471
+ - Use when: Multiple correct answers exist, testing thorough knowledge
101472
+ - Example: "Select all factors that influence plant growth" (analyzing components)
101473
+
101474
+ **FILL IN THE BLANKS (fill_in_the_blanks)**
101475
+ - Best for: Key terminology, formulas, specific facts
101476
+ - Bloom levels: Remembering, Understanding
101477
+ - Use when: Testing precise recall of important terms/concepts
101478
+ - Example: "The process of _____ converts light energy to chemical energy"
101479
+
101480
+ **NUMERIC (numeric)**
101481
+ - Best for: Calculations, quantitative problems, formula application
101482
+ - Bloom levels: Applying, Analyzing
101483
+ - Use when: Mathematical computations are required
101484
+ - Example: "Calculate the molarity of a 2L solution containing 0.5 moles of NaCl"
101485
+
101486
+ **MATCHING (matching)**
101487
+ - Best for: Connecting related concepts, terminology pairs
101488
+ - Bloom levels: Remembering, Understanding
101489
+ - Use when: Testing relationships between terms and definitions
101490
+ - Example: Match organelles with their functions
101491
+
101492
+ **SEQUENCE (sequence)**
101493
+ - Best for: Process steps, chronological order, procedural knowledge
101494
+ - Bloom levels: Understanding, Applying, Analyzing
101495
+ - Use when: Order or sequence is critical to understanding
101496
+ - Example: "Arrange the steps of mitosis in correct order"
101497
+
101498
+ **DRAG AND DROP (drag_and_drop)**
101499
+ - Best for: Categorization, classification, spatial relationships
101500
+ - Bloom levels: Understanding, Applying, Analyzing
101501
+ - Use when: Grouping or organizing information is key
101502
+ - Example: "Classify these compounds as acids, bases, or neutral"
101503
+
101504
+ **SHORT ANSWER (short_answer)**
101505
+ - Best for: Explanations, definitions, problem-solving steps
101506
+ - Bloom levels: Understanding, Applying, Analyzing, Evaluating, Creating
101507
+ - Use when: Requiring explanatory responses, open-ended thinking
101508
+ - Example: "Explain why enzymes are specific to certain substrates"
101509
+
101510
+ **CODING (coding)**
101511
+ - Best for: Programming problems, algorithm implementation
101512
+ - Bloom levels: Applying, Analyzing, Evaluating, Creating
101513
+ - Use when: Technical implementation or code analysis is required
101514
+ - Example: "Write a function to calculate factorial recursively"
101515
+ `;
101516
+ }
101517
+ function generateAdvancedBloomGuidance() {
101518
+ return `
101519
+ ADVANCED BLOOM'S TAXONOMY & QUESTION TYPE OPTIMIZATION:
101520
+
101521
+ **REMEMBERING (Knowledge Recall)**
101522
+ - Primary types: true_false, fill_in_the_blanks, matching
101523
+ - Secondary types: multiple_choice (simple recall)
101524
+ - Focus: Facts, terms, basic concepts, definitions
101525
+ - Misconception addressing: Use true_false to clarify common confusions
101526
+
101527
+ **UNDERSTANDING (Comprehension)**
101528
+ - Primary types: multiple_choice, short_answer, matching
101529
+ - Secondary types: multiple_response, sequence
101530
+ - Focus: Explanations, interpretations, examples, classifications
101531
+ - Best for: "Explain why...", "What does this mean...", "Give an example..."
101532
+
101533
+ **APPLYING (Using Knowledge)**
101534
+ - Primary types: numeric, short_answer, sequence, coding
101535
+ - Secondary types: multiple_choice, drag_and_drop
101536
+ - Focus: Problem-solving, implementing procedures, using methods
101537
+ - Best for: Calculations, step-by-step processes, practical applications
101538
+
101539
+ **ANALYZING (Breaking Down Information)**
101540
+ - Primary types: multiple_response, short_answer, sequence, coding
101541
+ - Secondary types: drag_and_drop, multiple_choice
101542
+ - Focus: Identifying components, relationships, cause-effect
101543
+ - Best for: "What are the factors...", "How do these relate...", "Break down..."
101544
+
101545
+ **EVALUATING (Making Judgments)**
101546
+ - Primary types: short_answer, multiple_response, coding
101547
+ - Secondary types: multiple_choice (with justification)
101548
+ - Focus: Critiquing, judging, comparing alternatives, decision-making
101549
+ - Best for: "Which is better and why...", "Evaluate the approach...", "Critique..."
101550
+
101551
+ **CREATING (Producing New Work)**
101552
+ - Primary types: short_answer, coding, sequence
101553
+ - Secondary types: drag_and_drop (design tasks)
101554
+ - Focus: Designing, planning, producing, constructing
101555
+ - Best for: "Design a solution...", "Create a plan...", "Develop a strategy..."
101556
+ `;
101557
+ }
101558
+ function generateDiversityRules() {
101559
+ return `
101560
+ ENHANCED DIVERSITY & QUALITY ASSURANCE RULES:
101561
+
101562
+ **Question Type Distribution Strategy:**
101563
+ 1. Never place more than 3 consecutive questions of the same type
101564
+ 2. Distribute question types based on their cognitive complexity
101565
+ 3. For quizzes with 10+ questions, use at least 4 different question types
101566
+ 4. For quizzes with 20+ questions, use at least 6 different question types
101567
+ 5. Balance quick-answer types (true_false, multiple_choice) with deeper types (short_answer, coding)
101568
+
101569
+ **Intelligent Difficulty Progression:**
101570
+ 1. Opening (20%): Start with confidence-building questions (Remembering/Understanding)
101571
+ 2. Building (40%): Gradually increase complexity (Understanding/Applying)
101572
+ 3. Peak (30%): Most challenging questions (Analyzing/Evaluating/Creating)
101573
+ 4. Closing (10%): Moderate challenge to end positively
101574
+
101575
+ **Misconception-Driven Planning:**
101576
+ - When common misconceptions are provided, prioritize question types that can effectively address them
101577
+ - Use true_false for binary misconceptions
101578
+ - Use multiple_choice for concept selection misconceptions
101579
+ - Use short_answer for complex misconceptions requiring explanation
101580
+
101581
+ **Contextual Intelligence:**
101582
+ - Programming/Technical topics: Favor coding, numeric, short_answer
101583
+ - Process-oriented topics: Favor sequence, short_answer, drag_and_drop
101584
+ - Conceptual topics: Favor multiple_choice, multiple_response, true_false
101585
+ - Factual topics: Favor fill_in_the_blanks, matching, true_false
101586
+ `;
101587
+ }
101588
+ async function generateQuizPlan(clientInput, apiKey, imageContexts = []) {
101589
+ const logger = new QuizPlanLogger();
101590
+ try {
101591
+ logger.log("VALIDATION_START", {
101592
+ totalQuestions: clientInput.totalQuestions,
101593
+ availableTypes: clientInput.selectedQuestionTypes,
101594
+ topicCount: clientInput.topics.length,
101595
+ bloomLevelCount: clientInput.bloomLevels.length
101596
+ });
101597
+ const totalTopicRatio = clientInput.topics.reduce((sum, t4) => sum + t4.ratio, 0);
101598
+ if (Math.abs(totalTopicRatio - 100) > 1) {
101599
+ throw new Error(`Total topic ratio must be 100%. Current sum: ${totalTopicRatio.toFixed(1)}%`);
101600
+ }
101601
+ const totalBloomRatio = clientInput.bloomLevels.reduce((sum, b2) => sum + b2.ratio, 0);
101602
+ if (Math.abs(totalBloomRatio - 100) > 1) {
101603
+ throw new Error(`Total Bloom level ratio must be 100%. Current sum: ${totalBloomRatio.toFixed(1)}%`);
101604
+ }
101605
+ logger.log("VALIDATION_SUCCESS", {
101606
+ topicRatioSum: totalTopicRatio,
101607
+ bloomRatioSum: totalBloomRatio
101608
+ });
101609
+ const aiStartTime = Date.now();
101610
+ const ai = new GoogleGenAI({
101611
+ apiKey
101612
+ });
101613
+ const model = "gemini-2.5-pro";
101614
+ const config3 = {
101615
+ temperature: 0.8,
101616
+ thinkingConfig: {
101617
+ thinkingBudget: 4096
101618
+ },
101619
+ responseMimeType: "application/json"
101620
+ };
101621
+ logger.log("AI_INITIALIZATION", { model }, Date.now() - aiStartTime);
101622
+ const { language: language3, totalQuestions, numCodingQuestions = 0 } = clientInput;
101623
+ const promptStartTime = Date.now();
101624
+ const topicsDistribution = clientInput.topics.map((t4) => {
101625
+ let topicString = `- Topic Context: "${t4.topic}", LoId: "${t4.originalLoId || "nil"}", Subject: "${t4.originalSubject || "nil"}", Category: "${t4.originalCategory || "nil"}", Topic: "${t4.originalTopic || "nil"}", Ratio: ${t4.ratio}%`;
101626
+ if (t4.commonMisconceptions && t4.commonMisconceptions.length > 0) {
101627
+ topicString += `
101628
+ - Common Misconceptions: [${t4.commonMisconceptions.join(", ")}]`;
101629
+ }
101630
+ return topicString;
101631
+ }).join("\n ");
101632
+ const bloomDistribution = clientInput.bloomLevels.map(
101633
+ (b2) => `- Level: "${b2.level}", Ratio: ${b2.ratio}%`
101634
+ ).join("\n ");
101635
+ let questionTypesForPrompt = [...clientInput.selectedQuestionTypes];
101636
+ if (numCodingQuestions === 0) {
101637
+ questionTypesForPrompt = questionTypesForPrompt.filter(
101638
+ (type) => type !== "short_answer" && type !== "coding"
101639
+ );
101640
+ }
101641
+ const allowedQuestionTypes = questionTypesForPrompt.map((t4) => `'${t4}'`).join(", ");
101642
+ const codingRequirement = numCodingQuestions > 0 ? `
101643
+ **CRITICAL CODING REQUIREMENT**: Exactly ${numCodingQuestions} questions in the plan MUST be of type 'coding'. These should typically be at 'applying' or higher Bloom levels and focus on implementation, debugging, or algorithm design.` : "";
101644
+ const imageContextSection = imageContexts && imageContexts.length > 0 ? `
101645
+ ## AVAILABLE IMAGE CONTEXT LIBRARY
101646
+ You have access to a library of pre-described images. When planning a question, if its subject, category, and topic match an image in this library AND the image's description is relevant to the planned question's topic, you MUST include the corresponding \`imageId\` in your output. Otherwise, leave the \`imageId\` field null or omit it.
101647
+
101648
+ \`\`\`json
101649
+ ${JSON.stringify(imageContexts.map((img) => ({ imageId: img.id, subject: img.subject, category: img.category, topic: img.topic, description: img.detailedDescription })), null, 2)}
101650
+ \`\`\`
101651
+ ` : "";
101652
+ const enhancedPromptText = `You are an elite educational assessment architect with expertise in cognitive science and adaptive learning. Your mission is to create a strategically optimized quiz plan that maximizes learning effectiveness.
101653
+
101654
+ ${generateQuestionTypeSelectionGuidance()}
101655
+
101656
+ ${generateAdvancedBloomGuidance()}
101657
+
101658
+ ${generateDiversityRules()}
101659
+
101660
+ ## COMPREHENSIVE QUIZ REQUIREMENTS:
101661
+
101662
+ **Target Language**: ${language3}
101663
+ **Total Questions**: ${totalQuestions}${codingRequirement}
101664
+
101665
+ **Topic Distribution & Learning Context** (follow precisely):
101666
+ ${topicsDistribution}
101667
+
101668
+ **Cognitive Complexity Distribution** (follow precisely):
101669
+ ${bloomDistribution}
101670
+
101671
+ **Available Question Arsenal**: ${allowedQuestionTypes}
101672
+
101673
+ **Image Resources**: ${imageContextSection}
101674
+
101675
+ ## STRATEGIC PLANNING METHODOLOGY:
101676
+
101677
+ 1. **Misconception Analysis**: If common misconceptions are provided, design questions specifically to address and correct them
101678
+ 2. **Question Type Intelligence**: Select question types based on the cognitive demands and content nature
101679
+ 3. **Difficulty Orchestration**: Create a learning journey that builds confidence while challenging appropriately
101680
+ 4. **Diversity Optimization**: Ensure variety prevents monotony and maintains engagement
101681
+ 5. **Context Sensitivity**: Match question types to topic characteristics (technical, conceptual, procedural)
101682
+ 6. **Image Context Integration**: Intelligently associate questions with relevant images from the provided library to enhance contextual understanding.
101683
+
101684
+ ## ENHANCED OUTPUT FORMAT:
101685
+
101686
+ Return ONLY a JSON object with this EXACT structure:
101687
+
101688
+ \`\`\`json
101689
+ {
101690
+ "quizPlan": [
101691
+ {
101692
+ "plannedTopic": "Specific, assessable topic derived from provided context",
101693
+ "plannedQuestionType": "question_type_from_allowed_list",
101694
+ "plannedBloomLevel": "bloom_level_from_requirements",
101695
+ "plannedContextId": "THEO_ABS",
101696
+ "imageId": "imgctx_12345abcde", // or null
101697
+ "targetMisconception": "Specific misconception this question addresses (or 'none' if not applicable)",
101698
+ "difficultyReason": "Strategic explanation of difficulty choice and placement",
101699
+ "topicSpecificity": "broad|focused|specific",
101700
+ "originalLoId": "corresponding_LoId_from_input",
101701
+ "originalSubject": "corresponding_Subject_from_input",
101702
+ "originalCategory": "corresponding_Category_from_input",
101703
+ "originalTopic": "corresponding_Topic_from_input"
101704
+ }
101705
+ ],
101706
+ "diversityMetrics": {
101707
+ "questionTypeDistribution": {"type1": count1, "type2": count2},
101708
+ "bloomLevelDistribution": {"level1": count1, "level2": count2},
101709
+ "maxConsecutiveSameType": number,
101710
+ "difficultyProgression": "description of how difficulty progresses",
101711
+ "misconceptionCoverage": number_of_misconceptions_addressed
101712
+ },
101713
+ "planningStrategy": {
101714
+ "overallApproach": "Brief description of the strategic approach taken",
101715
+ "keyDecisions": ["Major decision 1", "Major decision 2", "Major decision 3"]
101716
+ }
101717
+ }
101718
+ \`\`\`
101719
+
101720
+ Execute this plan with pedagogical precision. The quiz should feel like a carefully crafted learning journey that challenges and educates simultaneously.`;
101721
+ logger.log("PROMPT_PREPARATION", {
101722
+ promptLength: enhancedPromptText.length,
101723
+ topicCount: clientInput.topics.length,
101724
+ misconceptionCount: clientInput.topics.reduce((sum, t4) => sum + (t4.commonMisconceptions?.length || 0), 0)
101725
+ }, Date.now() - promptStartTime);
101726
+ const generationStartTime = Date.now();
101727
+ const contents = [
101728
+ {
101729
+ role: "user",
101730
+ parts: [
101731
+ {
101732
+ text: enhancedPromptText
101733
+ }
101734
+ ]
101735
+ }
101736
+ ];
101737
+ const aiResult = await ai.models.generateContent({
101738
+ model,
101739
+ config: config3,
101740
+ contents
101741
+ });
101742
+ const response = aiResult;
101743
+ const generationDuration = Date.now() - generationStartTime;
101744
+ logger.log("AI_GENERATION", {
101745
+ responseLength: response.candidates?.[0]?.content?.parts?.[0]?.text?.length || 0,
101746
+ duration: generationDuration
101747
+ }, generationDuration);
101748
+ const processingStartTime = Date.now();
101749
+ const rawText = response.candidates?.[0]?.content?.parts?.[0]?.text || "";
101750
+ let jsonText = rawText;
101751
+ if (!rawText.trim().startsWith("{") && !rawText.trim().startsWith("[")) {
101752
+ jsonText = extractJsonFromMarkdown(rawText);
101753
+ }
101754
+ logger.log("JSON_EXTRACTION", {
101755
+ rawTextLength: rawText.length,
101756
+ extractedJsonLength: jsonText.length
101757
+ });
101758
+ const aiGeneratedContent = GenerateQuizPlanOutputSchema.parse(JSON.parse(jsonText));
101759
+ logger.log("SCHEMA_VALIDATION", { success: true }, Date.now() - processingStartTime);
101760
+ const validationStartTime = Date.now();
101761
+ if (aiGeneratedContent.quizPlan.length !== clientInput.totalQuestions) {
101762
+ throw new Error(`AI planned for ${aiGeneratedContent.quizPlan.length} questions, but ${clientInput.totalQuestions} were requested.`);
101763
+ }
101764
+ const invalidTypes = [];
101765
+ aiGeneratedContent.quizPlan.forEach((item, index3) => {
101766
+ if (!clientInput.selectedQuestionTypes.includes(item.plannedQuestionType)) {
101767
+ invalidTypes.push(`Question ${index3 + 1}: '${item.plannedQuestionType}'`);
101768
+ }
101769
+ });
101770
+ if (invalidTypes.length > 0) {
101771
+ throw new Error(`Invalid question types found: ${invalidTypes.join(", ")}`);
101772
+ }
101773
+ const codingQuestions = aiGeneratedContent.quizPlan.filter((q2) => q2.plannedQuestionType === "coding");
101774
+ if (numCodingQuestions > 0 && codingQuestions.length !== numCodingQuestions) {
101775
+ throw new Error(`Expected ${numCodingQuestions} coding questions, but got ${codingQuestions.length}`);
101776
+ }
101777
+ const diversityAnalysis = validateConsecutiveTypes(aiGeneratedContent.quizPlan);
101778
+ logger.log("VALIDATION_COMPLETE", {
101779
+ questionCount: aiGeneratedContent.quizPlan.length,
101780
+ codingQuestionCount: codingQuestions.length,
101781
+ maxConsecutiveType: diversityAnalysis.maxConsecutive,
101782
+ questionTypeDistribution: aiGeneratedContent.diversityMetrics?.questionTypeDistribution || {}
101783
+ }, Date.now() - validationStartTime);
101784
+ const finalResult = {
101785
+ ...aiGeneratedContent,
101786
+ logs: logger.getLogs()
101787
+ };
101788
+ logger.log("GENERATION_COMPLETE", {
101789
+ totalDuration: logger.getTotalDuration(),
101790
+ success: true,
101791
+ finalQuestionCount: finalResult.quizPlan.length
101792
+ }, logger.getTotalDuration());
101793
+ console.log("\n=== QUIZ PLAN GENERATION SUMMARY ===");
101794
+ console.log(`\u2705 Successfully generated ${finalResult.quizPlan.length} questions`);
101795
+ console.log(`\u23F1\uFE0F Total generation time: ${logger.getTotalDuration()}ms`);
101796
+ console.log(`\u{1F3AF} Question types: ${Object.keys(finalResult.diversityMetrics?.questionTypeDistribution || {}).join(", ")}`);
101797
+ console.log(`\u{1F9E0} Bloom levels: ${Object.keys(finalResult.diversityMetrics?.bloomLevelDistribution || {}).join(", ")}`);
101798
+ if (numCodingQuestions > 0) {
101799
+ console.log(`\u{1F4BB} Coding questions: ${codingQuestions.length}/${numCodingQuestions} required`);
101800
+ }
101801
+ console.log(JSON.stringify(finalResult));
101802
+ console.log("=====================================\n");
101803
+ return finalResult;
101804
+ } catch (error) {
101805
+ logger.log("ERROR", {
101806
+ message: error.message,
101807
+ stack: error.stack,
101808
+ totalDuration: logger.getTotalDuration()
101809
+ });
101810
+ console.error("\u274C Quiz Plan Generation Failed:", error.message);
101811
+ console.error("\u{1F4CB} Full logs available in returned object");
101812
+ throw new Error(`Failed to generate Quiz Plan: ${error.message}`);
101813
+ }
101814
+ }
101815
+ function validateConsecutiveTypes(quizPlan) {
101816
+ let maxConsecutive = 1;
101817
+ let maxType = quizPlan[0]?.plannedQuestionType || "";
101818
+ let maxStartIndex = 0;
101819
+ let currentConsecutive = 1;
101820
+ let currentType = quizPlan[0]?.plannedQuestionType || "";
101821
+ let currentStartIndex = 0;
101822
+ for (let i2 = 1; i2 < quizPlan.length; i2++) {
101823
+ if (quizPlan[i2].plannedQuestionType === currentType) {
101824
+ currentConsecutive++;
101825
+ } else {
101826
+ if (currentConsecutive > maxConsecutive) {
101827
+ maxConsecutive = currentConsecutive;
101828
+ maxType = currentType;
101829
+ maxStartIndex = currentStartIndex;
101830
+ }
101831
+ currentConsecutive = 1;
101832
+ currentType = quizPlan[i2].plannedQuestionType;
101833
+ currentStartIndex = i2;
101834
+ }
101835
+ }
101836
+ if (currentConsecutive > maxConsecutive) {
101837
+ maxConsecutive = currentConsecutive;
101838
+ maxType = currentType;
101839
+ maxStartIndex = currentStartIndex;
101840
+ }
101841
+ return {
101842
+ maxConsecutive,
101843
+ type: maxType,
101844
+ startIndex: maxStartIndex
101845
+ };
101846
+ }
101847
+
101848
+ // src/ai/flows/generate-questions-from-quiz-plan.ts
101849
+ init_react_shim();
101850
+
101851
+ // src/services/TopicDataService.ts
101852
+ init_react_shim();
101853
+ var TopicDataService = class {
101854
+ static saveData(data) {
101855
+ try {
101856
+ if (typeof window === "undefined") return;
101857
+ const serializedData = JSON.stringify(data);
101858
+ localStorage.setItem(this.STORAGE_KEY, serializedData);
101859
+ } catch (error) {
101860
+ console.error("Error saving learning objectives to Local Storage:", error);
101861
+ }
101862
+ }
101863
+ static mergeData(newData) {
101864
+ const existingData = this.getData();
101865
+ const loMap = new Map(existingData.map((lo) => [lo.code, lo]));
101866
+ newData.forEach((newLo) => {
101867
+ loMap.set(newLo.code, newLo);
101868
+ });
101869
+ const mergedData = Array.from(loMap.values());
101870
+ this.saveData(mergedData);
101871
+ }
101872
+ static getData() {
101873
+ try {
101874
+ if (typeof window === "undefined") return [];
101875
+ const storedData = localStorage.getItem(this.STORAGE_KEY);
101876
+ return storedData ? JSON.parse(storedData) : [];
101877
+ } catch (error) {
101878
+ console.error("Error retrieving learning objectives from Local Storage:", error);
101879
+ this.clearData();
101880
+ return [];
101881
+ }
101882
+ }
101883
+ static clearData() {
101884
+ try {
101885
+ if (typeof window === "undefined") return;
101886
+ localStorage.removeItem(this.STORAGE_KEY);
101887
+ } catch (error) {
101888
+ console.error("Error clearing learning objectives from Local Storage:", error);
101889
+ }
101890
+ }
101891
+ static parseTSV(tsvContent) {
101892
+ const lines = tsvContent.split("\n").filter((line) => line.trim() !== "");
101893
+ if (lines.length < 2) {
101894
+ return { data: [], errors: ["File is empty or contains only a header."] };
101895
+ }
101896
+ const headerLine = lines.shift();
101897
+ const headers = headerLine.split(" ").map((h3) => h3.trim());
101898
+ if (headers.length !== this.EXPECTED_HEADERS.length || !this.EXPECTED_HEADERS.every((h3, i2) => h3 === headers[i2])) {
101899
+ const errorMsg = `Invalid TSV header. Expected: "${this.EXPECTED_HEADERS.join(" ")}". Received: "${headers.join(" ")}"`;
101900
+ return { data: [], errors: [errorMsg] };
101901
+ }
101902
+ const data = [];
101903
+ const errors2 = [];
101904
+ lines.forEach((line, index3) => {
101905
+ const values = line.split(" ").map((v) => v.trim());
101906
+ if (values.length !== this.EXPECTED_HEADERS.length) {
101907
+ errors2.push(`Line ${index3 + 2}: Incorrect number of columns. Expected ${this.EXPECTED_HEADERS.length}, but got ${values.length}.`);
101908
+ return;
101909
+ }
101910
+ const [
101911
+ loId,
101912
+ name3,
101913
+ loDescription,
101914
+ subject,
101915
+ category,
101916
+ topic,
101917
+ keywordsStr,
101918
+ grade,
101919
+ stemElementsStr,
101920
+ bloomLevelsStr
101921
+ ] = values;
101922
+ if (!loId || !loDescription || !subject || !category || !topic) {
101923
+ errors2.push(`Line ${index3 + 2}: Missing required fields (LO ID, LO Description, Subject, Category, or Topic).`);
101924
+ return;
101925
+ }
101926
+ const learningObjective = {
101927
+ id: generateUniqueId("lo_"),
101928
+ code: loId,
101929
+ name: name3,
101930
+ description: loDescription,
101931
+ // Can be the same as name or enhanced later
101932
+ subject,
101933
+ category,
101934
+ topic,
101935
+ grade,
101936
+ keywords: keywordsStr.split(",").map((k3) => k3.trim()).filter(Boolean),
101937
+ stemElements: stemElementsStr.split(",").map((s4) => s4.trim()).filter(Boolean),
101938
+ bloomLevelsGuideline: bloomLevelsStr.split(",").map((b2) => b2.trim()).filter(Boolean)
101939
+ };
101940
+ data.push(learningObjective);
101941
+ });
101942
+ return { data, errors: errors2 };
101943
+ }
101944
+ static getSubjects() {
101945
+ const data = this.getData();
101946
+ const subjects = data.map((item) => item.subject);
101947
+ return [...new Set(subjects)].sort();
101948
+ }
101949
+ static getCategoriesBySubject(subject) {
101950
+ const data = this.getData();
101951
+ const categories = data.filter((item) => item.subject === subject).map((item) => item.category);
101952
+ return [...new Set(categories)].sort();
101953
+ }
101954
+ static getTopicsByCategory(category) {
101955
+ const data = this.getData();
101956
+ const topics = data.filter((item) => item.category === category).map((item) => item.topic);
101957
+ return [...new Set(topics)].sort();
101958
+ }
101959
+ static getLearningObjectivesByTopics(topics) {
101960
+ const data = this.getData();
101961
+ const topicSet = new Set(topics);
101962
+ return data.filter((item) => topicSet.has(item.topic));
101963
+ }
101964
+ };
101965
+ TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
101966
+ TopicDataService.EXPECTED_HEADERS = [
101967
+ "LO ID",
101968
+ "LO Description",
101969
+ "Subject",
101970
+ "Category",
101971
+ "Topic",
101972
+ "Keywords",
101973
+ "Grade",
101974
+ "STEM Element(s)",
101975
+ "Bloom\u2019s Level(s) Guideline"
101976
+ ];
101977
+
101963
101978
  // src/ai/flows/question-gen/generate-coding-question.ts
101964
101979
  init_react_shim();
101965
101980
 
@@ -101997,7 +102012,7 @@ Previous attempts failed. Pay strict attention to the JSON schema and all rules.
101997
102012
  const imageContextInstruction = imageUrl ? `**Image Context:** You MUST analyze the provided image. The coding problem must be directly related to processing or interpreting the content of this image.` : "";
101998
102013
  const contextStrings = [
101999
102014
  `**Subject:** ${subject}`,
102000
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
102015
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
102001
102016
  imageContextInstruction,
102002
102017
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
102003
102018
  quizContext?.targetMisconception && `**Target Misconception:** The problem should test against this common error: "${quizContext.targetMisconception}"`
@@ -102154,9 +102169,9 @@ var calculateCombinedDifficulty = (plannedQ) => {
102154
102169
  break;
102155
102170
  }
102156
102171
  const totalScore = bloomScore + contextScore + questionTypeScore;
102157
- if (totalScore <= 4) return "easy";
102158
- if (totalScore <= 7) return "medium";
102159
- return "hard";
102172
+ if (totalScore <= 4) return "Easy";
102173
+ if (totalScore <= 7) return "Medium";
102174
+ return "Hard";
102160
102175
  };
102161
102176
  async function generateQuestionsFromQuizPlan(clientInput, apiKey) {
102162
102177
  const { quizPlan, language: language3, imageContexts } = clientInput;
@@ -102169,7 +102184,7 @@ async function generateQuestionsFromQuizPlan(clientInput, apiKey) {
102169
102184
  let lastError = null;
102170
102185
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
102171
102186
  try {
102172
- const fullLO = plannedQ.originalLoId ? allLearningObjectives.find((lo) => lo.loId === plannedQ.originalLoId) : null;
102187
+ const fullLO = plannedQ.originalLoId ? allLearningObjectives.find((lo) => lo.code === plannedQ.originalLoId) : null;
102173
102188
  const quizContext = {
102174
102189
  plannedTopic: plannedQ.plannedTopic,
102175
102190
  plannedQuestionType: plannedQ.plannedQuestionType,
@@ -102182,7 +102197,7 @@ async function generateQuestionsFromQuizPlan(clientInput, apiKey) {
102182
102197
  originalSubject: plannedQ.originalSubject,
102183
102198
  originalCategory: plannedQ.originalCategory,
102184
102199
  originalTopic: plannedQ.originalTopic,
102185
- loDescription: fullLO?.loDescription || plannedQ.plannedTopic
102200
+ description: fullLO?.description || plannedQ.plannedTopic
102186
102201
  };
102187
102202
  const imageUrl = plannedQ.imageId && imageContexts ? imageContexts.find((ctx) => ctx.id === plannedQ.imageId)?.imageUrl : void 0;
102188
102203
  const baseClientInput = {
@@ -102536,7 +102551,7 @@ var AIFullQuizGeneratorModal = ({
102536
102551
  };
102537
102552
  const renderContent3 = () => {
102538
102553
  if (currentStage === "review") {
102539
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 py-4" }, /* @__PURE__ */ React169__default.createElement("h3", { className: "text-lg font-medium mb-2 text-primary flex items-center" }, /* @__PURE__ */ React169__default.createElement(Eye, { className: "mr-2 h-5 w-5" }), " Review & Adjust AI Generated Quiz Plan"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, "The AI has proposed the following plan. You can change question types, reorder, or remove questions before final generation. Note: 'Drag and Drop' type questions in the plan will be created as placeholders and require manual authoring of items/zones/answers."), aiGeneratedPlan && aiGeneratedPlan.length > 0 ? /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "max-h-[calc(60vh - 120px)] pr-3" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, aiGeneratedPlan.map((plannedQ, index3) => /* @__PURE__ */ React169__default.createElement(Card, { key: `planned-${index3}-${plannedQ.plannedTopic.replace(/\s/g, "")}`, className: "p-3" }, /* @__PURE__ */ React169__default.createElement(CardContent, { className: "p-0 space-y-2" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold text-sm" }, "Q", index3 + 1, ": ", plannedQ.plannedTopic), /* @__PURE__ */ React169__default.createElement("div", { className: "flex space-x-1" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "up"), disabled: index3 === 0 }, /* @__PURE__ */ React169__default.createElement(ArrowUp, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "down"), disabled: index3 === aiGeneratedPlan.length - 1 }, /* @__PURE__ */ React169__default.createElement(ArrowDown, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7 text-destructive", onClick: () => handleRemovePlannedQuestion(index3) }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-3 items-end" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `review-qtype-${index3}`, className: "text-xs" }, "Question Type"), /* @__PURE__ */ React169__default.createElement(
102554
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4 py-4" }, /* @__PURE__ */ React169__default.createElement("h3", { className: "text-lg font-Medium mb-2 text-primary flex items-center" }, /* @__PURE__ */ React169__default.createElement(Eye, { className: "mr-2 h-5 w-5" }), " Review & Adjust AI Generated Quiz Plan"), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, "The AI has proposed the following plan. You can change question types, reorder, or remove questions before final generation. Note: 'Drag and Drop' type questions in the plan will be created as placeholders and require manual authoring of items/zones/answers."), aiGeneratedPlan && aiGeneratedPlan.length > 0 ? /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "max-h-[calc(60vh - 120px)] pr-3" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, aiGeneratedPlan.map((plannedQ, index3) => /* @__PURE__ */ React169__default.createElement(Card, { key: `planned-${index3}-${plannedQ.plannedTopic.replace(/\s/g, "")}`, className: "p-3" }, /* @__PURE__ */ React169__default.createElement(CardContent, { className: "p-0 space-y-2" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold text-sm" }, "Q", index3 + 1, ": ", plannedQ.plannedTopic), /* @__PURE__ */ React169__default.createElement("div", { className: "flex space-x-1" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "up"), disabled: index3 === 0 }, /* @__PURE__ */ React169__default.createElement(ArrowUp, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "down"), disabled: index3 === aiGeneratedPlan.length - 1 }, /* @__PURE__ */ React169__default.createElement(ArrowDown, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7 text-destructive", onClick: () => handleRemovePlannedQuestion(index3) }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-3 items-end" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `review-qtype-${index3}`, className: "text-xs" }, "Question Type"), /* @__PURE__ */ React169__default.createElement(
102540
102555
  Select2,
102541
102556
  {
102542
102557
  value: plannedQ.plannedQuestionType,
@@ -102565,7 +102580,7 @@ var AIFullQuizGeneratorModal = ({
102565
102580
  min: "1",
102566
102581
  max: "100"
102567
102582
  }
102568
- )), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-medium px-1" }, "Topic Distribution"), topics.map((topicItem, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: topicItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__default.createElement(
102583
+ )), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Topic Distribution"), topics.map((topicItem, index3) => /* @__PURE__ */ React169__default.createElement("div", { key: topicItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__default.createElement(
102569
102584
  Input,
102570
102585
  {
102571
102586
  type: "text",
@@ -102585,7 +102600,7 @@ var AIFullQuizGeneratorModal = ({
102585
102600
  min: "0",
102586
102601
  max: "100"
102587
102602
  }
102588
- ), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeTopic(topicItem.id), disabled: topics.length <= 1 }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: addTopic }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic")), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-medium px-1" }, "Bloom Level Distribution"), bloomLevels.map((bloomItem) => /* @__PURE__ */ React169__default.createElement("div", { key: bloomItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "w-32 capitalize flex-shrink-0" }, bloomItem.level), /* @__PURE__ */ React169__default.createElement(
102603
+ ), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeTopic(topicItem.id), disabled: topics.length <= 1 }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: addTopic }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic")), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Bloom Level Distribution"), bloomLevels.map((bloomItem) => /* @__PURE__ */ React169__default.createElement("div", { key: bloomItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "w-32 capitalize flex-shrink-0" }, bloomItem.level), /* @__PURE__ */ React169__default.createElement(
102589
102604
  Input,
102590
102605
  {
102591
102606
  type: "number",
@@ -102596,7 +102611,7 @@ var AIFullQuizGeneratorModal = ({
102596
102611
  min: "0",
102597
102612
  max: "100"
102598
102613
  }
102599
- )))), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-medium px-1" }, "Relevant Contexts (Optional, Select Multiple)"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, contextOptions.filter((opt) => opt.contextId !== "__none__" && opt.contextId !== "__custom__").map((opt) => /* @__PURE__ */ React169__default.createElement("div", { key: opt.contextId, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(
102614
+ )))), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Relevant Contexts (Optional, Select Multiple)"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, contextOptions.filter((opt) => opt.contextId !== "__none__" && opt.contextId !== "__custom__").map((opt) => /* @__PURE__ */ React169__default.createElement("div", { key: opt.contextId, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(
102600
102615
  Checkbox2,
102601
102616
  {
102602
102617
  id: `ctx-full-${opt.contextId}`,
@@ -102618,7 +102633,7 @@ var AIFullQuizGeneratorModal = ({
102618
102633
  placeholder: "Enter your specific custom context here...",
102619
102634
  className: "min-h-[60px] mt-2 text-sm"
102620
102635
  }
102621
- )), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-medium px-1" }, "Question Types to Use (Select Multiple)"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__default.createElement("div", { key: qType.value, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(
102636
+ )), /* @__PURE__ */ React169__default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Question Types to Use (Select Multiple)"), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__default.createElement("div", { key: qType.value, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(
102622
102637
  Checkbox2,
102623
102638
  {
102624
102639
  id: `qtype-full-${qType.value}`,
@@ -103018,7 +103033,7 @@ var AlertTitle = React169.forwardRef(({ className, ...props }, ref) => /* @__PUR
103018
103033
  "h5",
103019
103034
  {
103020
103035
  ref,
103021
- className: cn("mb-1 font-medium leading-none tracking-tight", className),
103036
+ className: cn("mb-1 font-Medium leading-none tracking-tight", className),
103022
103037
  ...props
103023
103038
  }
103024
103039
  ));
@@ -103063,7 +103078,7 @@ var BaseRawQuestionSchema = z.object({
103063
103078
  points: z.number().optional(),
103064
103079
  explanation: z.string().optional(),
103065
103080
  topic: z.string().optional(),
103066
- difficulty: z.enum(["easy", "medium", "hard"]).optional(),
103081
+ difficulty: z.enum(["Easy", "Medium", "Hard"]).optional(),
103067
103082
  bloomLevel: z.string().optional()
103068
103083
  });
103069
103084
  var RawMCQSchema = BaseRawQuestionSchema.extend({
@@ -103416,7 +103431,7 @@ var QuizEditorService = class {
103416
103431
  questionType: type,
103417
103432
  prompt: "",
103418
103433
  points: 10,
103419
- difficulty: "medium"
103434
+ difficulty: "Medium"
103420
103435
  };
103421
103436
  switch (type) {
103422
103437
  case "true_false":
@@ -103643,7 +103658,7 @@ var SelectedQuestionsPanel = ({
103643
103658
  },
103644
103659
  /* @__PURE__ */ React169__default.createElement(ArrowDown, { className: "h-3 w-3" })
103645
103660
  )),
103646
- /* @__PURE__ */ React169__default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React169__default.createElement("div", { className: "font-medium text-sm leading-5 break-words" }, index3 + 1, ".", " ", q2.prompt.replace(/<[^>]*>?/gm, "").substring(0, 100) || `(${q2.questionType} - Untitled)`), /* @__PURE__ */ React169__default.createElement("div", { className: "text-xs text-muted-foreground mt-1" }, q2.questionType, " \u2022 ", q2.points || 0, " pts")),
103661
+ /* @__PURE__ */ React169__default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React169__default.createElement("div", { className: "font-Medium text-sm leading-5 break-words" }, index3 + 1, ".", " ", q2.prompt.replace(/<[^>]*>?/gm, "").substring(0, 100) || `(${q2.questionType} - Untitled)`), /* @__PURE__ */ React169__default.createElement("div", { className: "text-xs text-muted-foreground mt-1" }, q2.questionType, " \u2022 ", q2.points || 0, " pts")),
103647
103662
  /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-1 flex-shrink-0" }, /* @__PURE__ */ React169__default.createElement(
103648
103663
  Button,
103649
103664
  {
@@ -104030,7 +104045,7 @@ var TableFooter = React169.forwardRef(({ className, ...props }, ref) => /* @__PU
104030
104045
  {
104031
104046
  ref,
104032
104047
  className: cn(
104033
- "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
104048
+ "border-t bg-muted/50 font-Medium [&>tr]:last:border-b-0",
104034
104049
  className
104035
104050
  ),
104036
104051
  ...props
@@ -104054,7 +104069,7 @@ var TableHead = React169.forwardRef(({ className, ...props }, ref) => /* @__PURE
104054
104069
  {
104055
104070
  ref,
104056
104071
  className: cn(
104057
- "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
104072
+ "h-12 px-4 text-left align-middle font-Medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
104058
104073
  className
104059
104074
  ),
104060
104075
  ...props
@@ -104144,20 +104159,6 @@ var questionTypeManager = new LocalStorageManager("question_types");
104144
104159
  var learningObjectiveManager = new LocalStorageManager("learning_objectives");
104145
104160
  var contextManager = new LocalStorageManager("contexts");
104146
104161
  var approachManager = new LocalStorageManager("approaches");
104147
- function mapRawDifficultyToStandard(rawDifficulty) {
104148
- switch (rawDifficulty) {
104149
- case "E":
104150
- case "E~M":
104151
- return "easy";
104152
- case "M":
104153
- case "M~H":
104154
- return "medium";
104155
- case "H":
104156
- return "hard";
104157
- default:
104158
- return "medium";
104159
- }
104160
- }
104161
104162
  var MetadataService = class {
104162
104163
  };
104163
104164
  // --- Subject Services ---
@@ -104215,15 +104216,10 @@ MetadataService.deleteContext = (code4) => contextManager.delete(code4);
104215
104216
  MetadataService.getApproaches = () => approachManager.getAll().sort((a4, b2) => a4.code.localeCompare(b2.code));
104216
104217
  MetadataService.saveApproaches = (items) => approachManager.saveAll(items);
104217
104218
  MetadataService.addApproach = (approachData) => {
104218
- const difficulty = mapRawDifficultyToStandard(approachData.rawDifficulty);
104219
- return approachManager.add({ ...approachData, difficulty });
104219
+ return approachManager.add(approachData);
104220
104220
  };
104221
104221
  MetadataService.updateApproach = (id3, approachData) => {
104222
- const updates = { ...approachData };
104223
- if (approachData.rawDifficulty) {
104224
- updates.difficulty = mapRawDifficultyToStandard(approachData.rawDifficulty);
104225
- }
104226
- return approachManager.update(id3, updates);
104222
+ return approachManager.update(id3, approachData);
104227
104223
  };
104228
104224
  MetadataService.deleteApproach = (code4) => approachManager.delete(code4);
104229
104225
  // --- LearningObjective Services ---
@@ -104233,8 +104229,12 @@ MetadataService.getLearningObjectives = (subjectCode) => {
104233
104229
  return filtered.sort((a4, b2) => a4.name.localeCompare(b2.name));
104234
104230
  };
104235
104231
  MetadataService.saveLearningObjectives = (items) => learningObjectiveManager.saveAll(items);
104236
- MetadataService.addLearningObjective = (name3, code4, subjectCode, description) => learningObjectiveManager.add({ name: name3, code: code4, subjectCode, description });
104237
- MetadataService.updateLearningObjective = (id3, name3, code4, subjectCode, description) => learningObjectiveManager.update(id3, { name: name3, code: code4, subjectCode, description });
104232
+ MetadataService.addLearningObjective = (item) => {
104233
+ return learningObjectiveManager.add(item);
104234
+ };
104235
+ MetadataService.updateLearningObjective = (id3, updates) => {
104236
+ return learningObjectiveManager.update(id3, updates);
104237
+ };
104238
104238
  MetadataService.deleteLearningObjective = (code4) => learningObjectiveManager.delete(code4);
104239
104239
 
104240
104240
  // node_modules/date-fns/addDays.mjs
@@ -106134,7 +106134,7 @@ function QuestionList({
106134
106134
  if (questions.length === 0) {
106135
106135
  return /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-8" }, "No questions match the current filters. Try adjusting your search or add new questions.");
106136
106136
  }
106137
- return /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, { className: "w-[30%]" }, "Question Text"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Type"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Topic"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Grade"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Bloom's"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Last Modified"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, questions.map((question2) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: question2.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium max-w-xs truncate", title: question2.text }, /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: question2.questionConfig.prompt })), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, question2.code), /* @__PURE__ */ React169__default.createElement(TableCell, null, /* @__PURE__ */ React169__default.createElement(Badge2, { variant: "secondary" }, getLookupName(question2.questionTypeCode, metadata.questionTypes))), /* @__PURE__ */ React169__default.createElement(TableCell, null, getLookupName(question2.subjectCode, metadata.subjects)), /* @__PURE__ */ React169__default.createElement(TableCell, null, getLookupName(question2.topicCode, metadata.topics)), /* @__PURE__ */ React169__default.createElement(TableCell, null, getLookupName(question2.gradeLevelCode, metadata.gradeLevels)), /* @__PURE__ */ React169__default.createElement(TableCell, null, /* @__PURE__ */ React169__default.createElement(Badge2, { variant: "outline" }, getLookupName(question2.bloomLevelCode, metadata.bloomLevels))), /* @__PURE__ */ React169__default.createElement(TableCell, null, format(new Date(question2.lastModified), "MMM d, yyyy")), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, onView && /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onView(question2), className: "mr-1" }, /* @__PURE__ */ React169__default.createElement(Eye, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onEdit(question2), className: "mr-1" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onDelete(question2), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" }))))))));
106137
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, { className: "w-[30%]" }, "Question Text"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Type"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Topic"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Grade"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Bloom's"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Last Modified"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, questions.map((question2) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: question2.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium max-w-xs truncate", title: question2.text }, /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: question2.questionConfig.prompt })), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, question2.code), /* @__PURE__ */ React169__default.createElement(TableCell, null, /* @__PURE__ */ React169__default.createElement(Badge2, { variant: "secondary" }, getLookupName(question2.questionTypeCode, metadata.questionTypes))), /* @__PURE__ */ React169__default.createElement(TableCell, null, getLookupName(question2.subjectCode, metadata.subjects)), /* @__PURE__ */ React169__default.createElement(TableCell, null, getLookupName(question2.topicCode, metadata.topics)), /* @__PURE__ */ React169__default.createElement(TableCell, null, getLookupName(question2.gradeLevelCode, metadata.gradeLevels)), /* @__PURE__ */ React169__default.createElement(TableCell, null, /* @__PURE__ */ React169__default.createElement(Badge2, { variant: "outline" }, getLookupName(question2.bloomLevelCode, metadata.bloomLevels))), /* @__PURE__ */ React169__default.createElement(TableCell, null, format(new Date(question2.lastModified), "MMM d, yyyy")), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, onView && /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onView(question2), className: "mr-1" }, /* @__PURE__ */ React169__default.createElement(Eye, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onEdit(question2), className: "mr-1" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onDelete(question2), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" }))))))));
106138
106138
  }
106139
106139
 
106140
106140
  // src/react-ui/components/authoring/QuestionFilters.tsx
@@ -106215,7 +106215,7 @@ function QuestionFilters({
106215
106215
  onChange: (e3) => setSearchTerm(e3.target.value),
106216
106216
  className: "lg:col-span-2 xl:col-span-1"
106217
106217
  }
106218
- ), /* @__PURE__ */ React169__default.createElement(Select2, { value: subjectCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setSubjectCode) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Subject" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Subjects"), subjects.map((s4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: s4.code, value: s4.code }, s4.name)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: topicCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setTopicCode), disabled: !subjectCode || filteredTopics.length === 0 }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Topic" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Topics"), filteredTopics.map((t4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: t4.code, value: t4.code }, t4.name)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: gradeLevelCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setGradeLevelCode) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Grade Level" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Grade Levels"), gradeLevels.map((gl) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: gl.code, value: gl.code }, gl.name)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: bloomLevelCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setBloomLevelCode) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Bloom's Level" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Levels"), bloomLevels.map((bl) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: bl.code, value: bl.code }, bl.name)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: questionTypeCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setQuestionTypeCode) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Question Type" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Types"), questionTypes.map((qt) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: qt.code, value: qt.code }, qt.name)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: difficulty || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setDifficulty) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Difficulty" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Difficulties"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "easy" }, "Easy"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "medium" }, "Medium"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "hard" }, "Hard"))), /* @__PURE__ */ React169__default.createElement("div", { className: "flex gap-2 col-span-full sm:col-span-1 xl:col-span-2 xl:col-start-6" }, /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleApplyFilters, className: "w-full sm:w-auto flex-grow" }, /* @__PURE__ */ React169__default.createElement(Search, { className: "mr-2 h-4 w-4" }), " Apply"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleClearFilters, variant: "outline", className: "w-full sm:w-auto flex-grow" }, /* @__PURE__ */ React169__default.createElement(CircleX, { className: "mr-2 h-4 w-4" }), " Clear"))));
106218
+ ), /* @__PURE__ */ React169__default.createElement(Select2, { value: subjectCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setSubjectCode) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Subject" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Subjects"), subjects.map((s4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: s4.code, value: s4.code }, s4.name)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: topicCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setTopicCode), disabled: !subjectCode || filteredTopics.length === 0 }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Topic" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Topics"), filteredTopics.map((t4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: t4.code, value: t4.code }, t4.name)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: gradeLevelCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setGradeLevelCode) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Grade Level" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Grade Levels"), gradeLevels.map((gl) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: gl.code, value: gl.code }, gl.name)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: bloomLevelCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setBloomLevelCode) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Bloom's Level" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Levels"), bloomLevels.map((bl) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: bl.code, value: bl.code }, bl.name)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: questionTypeCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setQuestionTypeCode) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Question Type" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Types"), questionTypes.map((qt) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: qt.code, value: qt.code }, qt.name)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: difficulty || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setDifficulty) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Difficulty" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Difficulties"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "Easy" }, "Easy"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "Medium" }, "Medium"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "Hard" }, "Hard"))), /* @__PURE__ */ React169__default.createElement("div", { className: "flex gap-2 col-span-full sm:col-span-1 xl:col-span-2 xl:col-start-6" }, /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleApplyFilters, className: "w-full sm:w-auto flex-grow" }, /* @__PURE__ */ React169__default.createElement(Search, { className: "mr-2 h-4 w-4" }), " Apply"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleClearFilters, variant: "outline", className: "w-full sm:w-auto flex-grow" }, /* @__PURE__ */ React169__default.createElement(CircleX, { className: "mr-2 h-4 w-4" }), " Clear"))));
106219
106219
  }
106220
106220
 
106221
106221
  // src/react-ui/components/authoring/QuestionFormDialog.tsx
@@ -106323,7 +106323,7 @@ function QuestionFormDialog({
106323
106323
  };
106324
106324
  const dialogTitle = questionToEdit ? "Edit Question Metadata" : "Create New Question";
106325
106325
  const dialogDescription = "First, define the metadata for the question. Then, edit the question's content and logic.";
106326
- return /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-xl md:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, dialogTitle), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, dialogDescription)), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "code" }, "Question Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "code", value: code4, onChange: (e3) => setCode(e3.target.value.toUpperCase()), placeholder: "Unique code (e.g., MATH-ALG-001)", disabled: !!questionToEdit })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "gradeLevelCode" }, "Grade Level"), /* @__PURE__ */ React169__default.createElement(Select2, { value: gradeLevelCode, onValueChange: setGradeLevelCode }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "gradeLevelCode" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select Grade Level" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, gradeLevels.map((gl) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: gl.code, value: gl.code }, gl.name)))))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject"), /* @__PURE__ */ React169__default.createElement(Select2, { value: subjectCode, onValueChange: setSubjectCode }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "subjectCode" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select Subject" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, subjects.map((s4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: s4.code, value: s4.code }, s4.name))))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "topicCode" }, "Topic"), /* @__PURE__ */ React169__default.createElement(Select2, { value: topicCode, onValueChange: setTopicCode, disabled: !subjectCode || filteredTopics.length === 0 }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "topicCode" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select Topic" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, filteredTopics.map((t4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: t4.code, value: t4.code }, t4.name)))))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "bloomLevelCode" }, "Bloom's Level"), /* @__PURE__ */ React169__default.createElement(Select2, { value: bloomLevelCode, onValueChange: setBloomLevelCode }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "bloomLevelCode" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select Bloom's Level" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, bloomLevels.map((bl) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: bl.code, value: bl.code }, bl.name))))), /* @__PURE__ */ React169__default.createElement("div", { className: "pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Question Content & Logic"), questionConfig ? /* @__PURE__ */ React169__default.createElement("div", { className: "p-3 mt-2 border rounded-md bg-muted/30 flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "font-medium" }, "Type: ", /* @__PURE__ */ React169__default.createElement("span", { className: "font-normal" }, questionConfig.questionType)), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground truncate max-w-md" }, "Prompt: ", questionConfig.prompt.replace(/<[^>]*>?/gm, "") || "Not set")), /* @__PURE__ */ React169__default.createElement(Button, { variant: "outline", onClick: () => handleOpenQuestionEditor() }, /* @__PURE__ */ React169__default.createElement(SquarePen, { className: "mr-2 h-4 w-4" }), " Edit Content")) : /* @__PURE__ */ React169__default.createElement("div", { className: "p-3 mt-2 border-dashed border-2 rounded-md text-center" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground mb-2" }, "No content has been created yet."), /* @__PURE__ */ React169__default.createElement(Button, { variant: "default", onClick: () => handleOpenQuestionEditor("multiple_choice") }, /* @__PURE__ */ React169__default.createElement(BookCopy, { className: "mr-2 h-4 w-4" }), " Create Question Content")))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !questionConfig }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save Question")))), questionConfig && /* @__PURE__ */ React169__default.createElement(
106326
+ return /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-xl md:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, dialogTitle), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, dialogDescription)), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "code" }, "Question Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "code", value: code4, onChange: (e3) => setCode(e3.target.value.toUpperCase()), placeholder: "Unique code (e.g., MATH-ALG-001)", disabled: !!questionToEdit })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "gradeLevelCode" }, "Grade Level"), /* @__PURE__ */ React169__default.createElement(Select2, { value: gradeLevelCode, onValueChange: setGradeLevelCode }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "gradeLevelCode" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select Grade Level" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, gradeLevels.map((gl) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: gl.code, value: gl.code }, gl.name)))))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject"), /* @__PURE__ */ React169__default.createElement(Select2, { value: subjectCode, onValueChange: setSubjectCode }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "subjectCode" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select Subject" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, subjects.map((s4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: s4.code, value: s4.code }, s4.name))))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "topicCode" }, "Topic"), /* @__PURE__ */ React169__default.createElement(Select2, { value: topicCode, onValueChange: setTopicCode, disabled: !subjectCode || filteredTopics.length === 0 }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "topicCode" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select Topic" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, filteredTopics.map((t4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: t4.code, value: t4.code }, t4.name)))))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "bloomLevelCode" }, "Bloom's Level"), /* @__PURE__ */ React169__default.createElement(Select2, { value: bloomLevelCode, onValueChange: setBloomLevelCode }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "bloomLevelCode" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select Bloom's Level" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, bloomLevels.map((bl) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: bl.code, value: bl.code }, bl.name))))), /* @__PURE__ */ React169__default.createElement("div", { className: "pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement(Label2, { className: "font-semibold" }, "Question Content & Logic"), questionConfig ? /* @__PURE__ */ React169__default.createElement("div", { className: "p-3 mt-2 border rounded-md bg-muted/30 flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "font-Medium" }, "Type: ", /* @__PURE__ */ React169__default.createElement("span", { className: "font-normal" }, questionConfig.questionType)), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground truncate max-w-md" }, "Prompt: ", questionConfig.prompt.replace(/<[^>]*>?/gm, "") || "Not set")), /* @__PURE__ */ React169__default.createElement(Button, { variant: "outline", onClick: () => handleOpenQuestionEditor() }, /* @__PURE__ */ React169__default.createElement(SquarePen, { className: "mr-2 h-4 w-4" }), " Edit Content")) : /* @__PURE__ */ React169__default.createElement("div", { className: "p-3 mt-2 border-dashed border-2 rounded-md text-center" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground mb-2" }, "No content has been created yet."), /* @__PURE__ */ React169__default.createElement(Button, { variant: "default", onClick: () => handleOpenQuestionEditor("multiple_choice") }, /* @__PURE__ */ React169__default.createElement(BookCopy, { className: "mr-2 h-4 w-4" }), " Create Question Content")))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !questionConfig }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save Question")))), questionConfig && /* @__PURE__ */ React169__default.createElement(
106327
106327
  EditQuestionModal,
106328
106328
  {
106329
106329
  isOpen: isQuestionEditorOpen,
@@ -110641,10 +110641,10 @@ var RoadmapService = class {
110641
110641
  }
110642
110642
  return null;
110643
110643
  }
110644
- static updateRoadmapItemStatus(loId, isCompleted) {
110644
+ static updateRoadmapItemStatus(code4, isCompleted) {
110645
110645
  const roadmap = this.getRoadmap();
110646
110646
  if (roadmap) {
110647
- const itemIndex = roadmap.items.findIndex((item) => item.loId === loId);
110647
+ const itemIndex = roadmap.items.findIndex((item) => item.code === code4);
110648
110648
  if (itemIndex > -1) {
110649
110649
  roadmap.items[itemIndex].isCompleted = isCompleted;
110650
110650
  this.saveRoadmap(roadmap);
@@ -111225,7 +111225,7 @@ z.object({
111225
111225
  startDate: z.string().describe("The start date for the analysis period in ISO format (YYYY-MM-DD)."),
111226
111226
  endDate: z.string().describe("The end date for the analysis period in ISO format (YYYY-MM-DD)."),
111227
111227
  allAvailableTopics: z.array(z.object({
111228
- loId: z.string(),
111228
+ code: z.string(),
111229
111229
  subject: z.string(),
111230
111230
  category: z.string(),
111231
111231
  topic: z.string()
@@ -111236,7 +111236,7 @@ var RoadmapItemSchema = z.object({
111236
111236
  topicName: z.string(),
111237
111237
  reason: z.string(),
111238
111238
  suggestedDifficulty: z.enum(["Very Easy", "Easy", "Medium", "Hard", "Expert"]),
111239
- loId: z.string(),
111239
+ code: z.string(),
111240
111240
  isCompleted: z.boolean()
111241
111241
  });
111242
111242
  var WeeklyRoadmapSchema = z.object({
@@ -111301,7 +111301,7 @@ All topic names and loIds in your "weeklyRoadmap" output MUST be chosen directly
111301
111301
  - **gamificationRemarks**: Write an encouraging message mentioning their achievements.
111302
111302
  2. **For "weeklyRoadmap":**
111303
111303
  - Create a 5-item roadmap for the upcoming week.
111304
- - Prioritize the "areasForImprovement" you identified. For each, find the corresponding entry in "All Available Topics" and use its "topicName" and "loId".
111304
+ - Prioritize the "areasForImprovement" you identified. For each, find the corresponding entry in "All Available Topics" and use its "topicName" and "code".
111305
111305
  - If more items are needed, select related topics from "All Available Topics".
111306
111306
 
111307
111307
  **IF Practice History IS EMPTY:**
@@ -111312,7 +111312,7 @@ All topic names and loIds in your "weeklyRoadmap" output MUST be chosen directly
111312
111312
  - **gamificationRemarks**: Write a general motivational message about starting to learn.
111313
111313
  2. **For "weeklyRoadmap":**
111314
111314
  - Create a 5-item "starter" roadmap.
111315
- - Select 5 diverse and foundational topics directly from the "All Available Topics" list. Use their exact "topicName" and "loId".
111315
+ - Select 5 diverse and foundational topics directly from the "All Available Topics" list. Use their exact "topicName" and "code".
111316
111316
  - For the "reason", explain that this is a good starting point to explore the subject.
111317
111317
 
111318
111318
  --- END LOGIC FLOW ---
@@ -111340,7 +111340,7 @@ The 'suggestedDifficulty' field MUST ALWAYS be one of these exact English string
111340
111340
  "topicName": "Topic C",
111341
111341
  "reason": "To strengthen your understanding of this key area.",
111342
111342
  "suggestedDifficulty": "Easy",
111343
- "loId": "lo-id-for-topic-c-from-the-list",
111343
+ "code": "lo-id-for-topic-c-from-the-list",
111344
111344
  "isCompleted": false
111345
111345
  }
111346
111346
  ]
@@ -111602,11 +111602,11 @@ init_react_shim();
111602
111602
  // src/ai/flows/assess-and-map-document-types.ts
111603
111603
  init_react_shim();
111604
111604
  var LearningObjectiveContextSchema = z.object({
111605
- loId: z.string(),
111605
+ code: z.string(),
111606
111606
  subject: z.string(),
111607
111607
  category: z.string(),
111608
111608
  topic: z.string(),
111609
- loDescription: z.string()
111609
+ description: z.string()
111610
111610
  });
111611
111611
  z.object({
111612
111612
  language: z.string().default("English"),
@@ -111614,7 +111614,7 @@ z.object({
111614
111614
  learningObjectives: z.array(LearningObjectiveContextSchema).min(1, { message: "At least one learning objective is required for mapping." })
111615
111615
  });
111616
111616
  var MappedLOSchema = z.object({
111617
- loId: z.string().describe("The exact loId from the provided learning objectives list that matches the document content."),
111617
+ code: z.string().describe("The exact code from the provided learning objectives list that matches the document content."),
111618
111618
  confidence: z.number().min(0).max(100).describe("A confidence score (0-100) of how well the document maps to this LO."),
111619
111619
  reasoning: z.string().describe("A brief explanation for why this mapping is relevant.")
111620
111620
  });
@@ -111650,7 +111650,7 @@ You are an expert curriculum analyst. Your task is to analyze a given document a
111650
111650
  1. **Overall Relevance Assessment:** Read the document content and compare it against the entire list of LOs. Assign an overall "relevanceScore" from 0 (completely unrelated) to 100 (perfectly aligned with one or more LOs).
111651
111651
 
111652
111652
  2. **Specific Mapping:** Identify which specific LOs from the list are directly addressed by the document. For each match you find, provide:
111653
- - The exact "loId" of the matched LO.
111653
+ - The exact "code" of the matched LO.
111654
111654
  - A "confidence" score (0-100) for that specific match.
111655
111655
  - A brief "reasoning" in ${language3} explaining why the document content maps to that LO.
111656
111656
 
@@ -111665,7 +111665,7 @@ Return a single, valid JSON object in this EXACT format. Do not include any othe
111665
111665
  "isFreestyleRecommended": false,
111666
111666
  "mappedLOs": [
111667
111667
  {
111668
- "loId": "SWIFT_FUNC_01",
111668
+ "code": "SWIFT_FUNC_01",
111669
111669
  "confidence": 95,
111670
111670
  "reasoning": "The document provides a detailed explanation of function syntax and default parameters, which directly aligns with this learning objective."
111671
111671
  }
@@ -111760,7 +111760,7 @@ Return the response as a single JSON object with a key "generatedQuestions" cont
111760
111760
  "correctTempOptionId": "A",
111761
111761
  "explanation": "The document states that mitochondria are the powerhouses of the cell, responsible for cellular respiration.",
111762
111762
  "points": 10,
111763
- "difficulty": "medium",
111763
+ "difficulty": "Medium",
111764
111764
  "topic": "Cell Biology"
111765
111765
  },
111766
111766
  {
@@ -111769,7 +111769,7 @@ Return the response as a single JSON object with a key "generatedQuestions" cont
111769
111769
  "correctAnswer": false,
111770
111770
  "explanation": "The text specifies that the cell wall is a feature of plant cells, not animal cells.",
111771
111771
  "points": 10,
111772
- "difficulty": "easy",
111772
+ "difficulty": "Easy",
111773
111773
  "topic": "Cell Biology"
111774
111774
  }
111775
111775
  ]
@@ -112602,7 +112602,7 @@ var QuizReview = ({
112602
112602
  };
112603
112603
  return /* @__PURE__ */ React169__default.createElement(Card, { className: "w-full max-w-4xl mx-auto shadow-xl" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-3xl font-headline text-center flex items-center justify-center" }, /* @__PURE__ */ React169__default.createElement(BookOpen, { className: "mr-3 h-8 w-8 text-primary" }), "AI-Powered Quiz Review"), /* @__PURE__ */ React169__default.createElement(CardDescription, { className: "text-center text-lg" }, "Let's break down your results and reinforce your learning.")), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React169__default.createElement(Card, { className: "bg-muted/30" }, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-xl flex items-center" }, /* @__PURE__ */ React169__default.createElement(Lightbulb, { className: "mr-2 h-5 w-5 text-yellow-500" }), "Key Concepts Summary")), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: reviewContent.overallSummary }))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("h3", { className: "text-xl font-semibold mb-2" }, "Detailed Question Analysis"), /* @__PURE__ */ React169__default.createElement(Accordion2, { type: "single", collapsible: true, className: "w-full" }, quizResult.questionResults.map((qResult, index3) => {
112604
112604
  const aiReview = getReviewForQuestion(qResult.questionId);
112605
- return /* @__PURE__ */ React169__default.createElement(AccordionItem2, { value: `item-${index3}`, key: qResult.questionId }, /* @__PURE__ */ React169__default.createElement(AccordionTrigger2, null, /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-between w-full pr-2" }, /* @__PURE__ */ React169__default.createElement("span", { className: "text-left font-medium" }, "Question ", index3 + 1), qResult.isCorrect ? /* @__PURE__ */ React169__default.createElement("span", { className: "text-sm text-green-600 font-medium flex items-center gap-1" }, /* @__PURE__ */ React169__default.createElement(CircleCheckBig, { className: "h-4 w-4" }), " Correct") : /* @__PURE__ */ React169__default.createElement("span", { className: "text-sm text-destructive font-medium flex items-center gap-1" }, /* @__PURE__ */ React169__default.createElement(CircleX, { className: "h-4 w-4" }), " Incorrect"))), /* @__PURE__ */ React169__default.createElement(AccordionContent2, { className: "space-y-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 border rounded-md bg-background" }, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold mb-2" }, "Original Question:"), /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: qResult.prompt })), qResult.questionType === "coding" ? renderCodingResult(qResult) : renderStandardResult(qResult), aiReview && /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 border-l-4 border-primary bg-primary/10 rounded-r-md" }, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold text-primary mb-2" }, "AI Tutor Explanation:"), /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: aiReview.explanation }))));
112605
+ return /* @__PURE__ */ React169__default.createElement(AccordionItem2, { value: `item-${index3}`, key: qResult.questionId }, /* @__PURE__ */ React169__default.createElement(AccordionTrigger2, null, /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-between w-full pr-2" }, /* @__PURE__ */ React169__default.createElement("span", { className: "text-left font-Medium" }, "Question ", index3 + 1), qResult.isCorrect ? /* @__PURE__ */ React169__default.createElement("span", { className: "text-sm text-green-600 font-Medium flex items-center gap-1" }, /* @__PURE__ */ React169__default.createElement(CircleCheckBig, { className: "h-4 w-4" }), " Correct") : /* @__PURE__ */ React169__default.createElement("span", { className: "text-sm text-destructive font-Medium flex items-center gap-1" }, /* @__PURE__ */ React169__default.createElement(CircleX, { className: "h-4 w-4" }), " Incorrect"))), /* @__PURE__ */ React169__default.createElement(AccordionContent2, { className: "space-y-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 border rounded-md bg-background" }, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold mb-2" }, "Original Question:"), /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: qResult.prompt })), qResult.questionType === "coding" ? renderCodingResult(qResult) : renderStandardResult(qResult), aiReview && /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 border-l-4 border-primary bg-primary/10 rounded-r-md" }, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold text-primary mb-2" }, "AI Tutor Explanation:"), /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: aiReview.explanation }))));
112606
112606
  }))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("h3", { className: "text-xl font-semibold mb-2" }, "Topics for Further Study"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-wrap gap-2" }, reviewContent.relatedTopics.map((topic, index3) => /* @__PURE__ */ React169__default.createElement(Badge2, { key: index3, variant: "secondary", className: "text-base px-3 py-1" }, topic))))), /* @__PURE__ */ React169__default.createElement(CardFooter, { className: "flex flex-col sm:flex-row justify-between gap-2" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "outline", onClick: onBackToResults, className: "w-full sm:w-auto" }, /* @__PURE__ */ React169__default.createElement(ArrowLeft, { className: "mr-2 h-4 w-4" }), "Back to Results"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: onExit, className: "w-full sm:w-auto" }, /* @__PURE__ */ React169__default.createElement(LogOut, { className: "mr-2 h-4 w-4" }), "Finish & Exit")));
112607
112607
  };
112608
112608
 
@@ -112649,7 +112649,7 @@ var PracticeHistoryTable = ({ history: history2, maxHeight = "400px" }) => {
112649
112649
  if (percentage >= 50) return "secondary";
112650
112650
  return "destructive";
112651
112651
  };
112652
- return /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, null, t4("history.title")), /* @__PURE__ */ React169__default.createElement(CardDescription, null, t4("history.description"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "w-full border rounded-md", style: { height: maxHeight } }, /* @__PURE__ */ React169__default.createElement(TooltipProvider2, { delayDuration: 100 }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, { className: "sticky top-0 bg-muted z-10" }, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, { className: "w-[120px]" }, t4("history.headers.date")), /* @__PURE__ */ React169__default.createElement(TableHead, null, t4("history.headers.topic")), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[80px]" }, t4("history.headers.score")), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[90px]" }, t4("history.headers.percentage")))), /* @__PURE__ */ React169__default.createElement(TableBody, null, history2.length > 0 ? history2.map((session) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: session.id, onClick: () => handleRowClick(session), className: "cursor-pointer" }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium text-xs text-muted-foreground" }, formatDate(session.timestamp)), /* @__PURE__ */ React169__default.createElement(TableCell, null, /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col gap-1.5" }, session.topics.map((topicInfo, index3) => /* @__PURE__ */ React169__default.createElement(Tooltip2, { key: index3 }, /* @__PURE__ */ React169__default.createElement(TooltipTrigger2, { asChild: true }, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold text-sm truncate" }, topicInfo.topic)), /* @__PURE__ */ React169__default.createElement(TooltipContent2, null, /* @__PURE__ */ React169__default.createElement("p", null, /* @__PURE__ */ React169__default.createElement("strong", null, t4("settingsModal.topics.tableHeaders.subject"), ":"), " ", topicInfo.subject), /* @__PURE__ */ React169__default.createElement("p", null, /* @__PURE__ */ React169__default.createElement("strong", null, t4("settingsModal.topics.tableHeaders.category"), ":"), " ", topicInfo.category)))))), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right font-mono text-sm" }, session.score !== null ? `${session.score}/${session.maxScore}` : "N/A"), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, session.percentage !== null && /* @__PURE__ */ React169__default.createElement(
112652
+ return /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, null, t4("history.title")), /* @__PURE__ */ React169__default.createElement(CardDescription, null, t4("history.description"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "w-full border rounded-md", style: { height: maxHeight } }, /* @__PURE__ */ React169__default.createElement(TooltipProvider2, { delayDuration: 100 }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, { className: "sticky top-0 bg-muted z-10" }, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, { className: "w-[120px]" }, t4("history.headers.date")), /* @__PURE__ */ React169__default.createElement(TableHead, null, t4("history.headers.topic")), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[80px]" }, t4("history.headers.score")), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[90px]" }, t4("history.headers.percentage")))), /* @__PURE__ */ React169__default.createElement(TableBody, null, history2.length > 0 ? history2.map((session) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: session.id, onClick: () => handleRowClick(session), className: "cursor-pointer" }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium text-xs text-muted-foreground" }, formatDate(session.timestamp)), /* @__PURE__ */ React169__default.createElement(TableCell, null, /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col gap-1.5" }, session.topics.map((topicInfo, index3) => /* @__PURE__ */ React169__default.createElement(Tooltip2, { key: index3 }, /* @__PURE__ */ React169__default.createElement(TooltipTrigger2, { asChild: true }, /* @__PURE__ */ React169__default.createElement("p", { className: "font-semibold text-sm truncate" }, topicInfo.topic)), /* @__PURE__ */ React169__default.createElement(TooltipContent2, null, /* @__PURE__ */ React169__default.createElement("p", null, /* @__PURE__ */ React169__default.createElement("strong", null, t4("settingsModal.topics.tableHeaders.subject"), ":"), " ", topicInfo.subject), /* @__PURE__ */ React169__default.createElement("p", null, /* @__PURE__ */ React169__default.createElement("strong", null, t4("settingsModal.topics.tableHeaders.category"), ":"), " ", topicInfo.category)))))), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right font-mono text-sm" }, session.score !== null ? `${session.score}/${session.maxScore}` : "N/A"), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, session.percentage !== null && /* @__PURE__ */ React169__default.createElement(
112653
112653
  Badge2,
112654
112654
  {
112655
112655
  variant: getPercentageBadgeVariant(session.percentage),
@@ -133464,12 +133464,12 @@ var ChartTooltipContent = React169.forwardRef(
133464
133464
  const itemConfig = getPayloadConfigFromPayload(config3, item, key);
133465
133465
  const value = !labelKey && typeof label === "string" ? config3[label]?.label || label : itemConfig?.label;
133466
133466
  if (labelFormatter) {
133467
- return /* @__PURE__ */ React169.createElement("div", { className: cn("font-medium", labelClassName) }, labelFormatter(value, payload));
133467
+ return /* @__PURE__ */ React169.createElement("div", { className: cn("font-Medium", labelClassName) }, labelFormatter(value, payload));
133468
133468
  }
133469
133469
  if (!value) {
133470
133470
  return null;
133471
133471
  }
133472
- return /* @__PURE__ */ React169.createElement("div", { className: cn("font-medium", labelClassName) }, value);
133472
+ return /* @__PURE__ */ React169.createElement("div", { className: cn("font-Medium", labelClassName) }, value);
133473
133473
  }, [
133474
133474
  label,
133475
133475
  labelFormatter,
@@ -133532,7 +133532,7 @@ var ChartTooltipContent = React169.forwardRef(
133532
133532
  )
133533
133533
  },
133534
133534
  /* @__PURE__ */ React169.createElement("div", { className: "grid gap-1.5" }, nestLabel ? tooltipLabel : null, /* @__PURE__ */ React169.createElement("span", { className: "text-muted-foreground" }, itemConfig?.label || item.name)),
133535
- item.value && /* @__PURE__ */ React169.createElement("span", { className: "font-mono font-medium tabular-nums text-foreground" }, item.value.toLocaleString())
133535
+ item.value && /* @__PURE__ */ React169.createElement("span", { className: "font-mono font-Medium tabular-nums text-foreground" }, item.value.toLocaleString())
133536
133536
  ))
133537
133537
  );
133538
133538
  }))
@@ -134709,7 +134709,7 @@ var ManageTopics = () => {
134709
134709
  return /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Card, { className: "w-full max-w-4xl mx-auto shadow-none border-none" }, /* @__PURE__ */ React169__default.createElement(CardHeader, { className: "px-1" }, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex items-center text-2xl font-headline" }, /* @__PURE__ */ React169__default.createElement(FileText, { className: "mr-3 h-6 w-6 text-primary" }), t4("settingsModal.topics.title")), /* @__PURE__ */ React169__default.createElement(CardDescription, null, t4("settingsModal.topics.description"))), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "space-y-6 px-1" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "tsv-importer", className: "text-lg font-semibold" }, t4("settingsModal.topics.importData")), /* @__PURE__ */ React169__default.createElement("div", { className: "mt-2 p-4 border border-dashed rounded-lg flex flex-col sm:flex-row items-center gap-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex-grow" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("settingsModal.topics.importHint")), /* @__PURE__ */ React169__default.createElement("a", { href: "#", className: "text-xs text-primary hover:underline", onClick: (e3) => {
134710
134710
  e3.preventDefault();
134711
134711
  alert("Header format:\nLO ID LO Description Subject Category Topic Keywords Grade STEM Element(s) Bloom\u2019s Level(s) Guideline");
134712
- } }, t4("settingsModal.topics.viewHeaderFormat"))), /* @__PURE__ */ React169__default.createElement(Input, { id: "tsv-importer", type: "file", ref: fileInputRef, accept: ".tsv,text/tab-separated-values", onChange: handleFileChange, className: "hidden" }), /* @__PURE__ */ React169__default.createElement(Button, { onClick: () => fileInputRef.current?.click() }, /* @__PURE__ */ React169__default.createElement(Upload, { className: "mr-2 h-4 w-4" }), t4("settingsModal.topics.chooseFile")))), importErrors.length > 0 && /* @__PURE__ */ React169__default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React169__default.createElement(CircleAlert, { className: "h-4 w-4" }), /* @__PURE__ */ React169__default.createElement(AlertTitle, null, t4("settingsModal.topics.importErrors")), /* @__PURE__ */ React169__default.createElement(AlertDescription, null, /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-24 mt-2" }, /* @__PURE__ */ React169__default.createElement("ul", { className: "list-disc list-inside text-xs space-y-1" }, importErrors.map((error, index3) => /* @__PURE__ */ React169__default.createElement("li", { key: index3 }, error)))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between mb-2" }, /* @__PURE__ */ React169__default.createElement("h3", { className: "text-lg font-semibold" }, t4("settingsModal.topics.currentDataTitle")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("settingsModal.topics.showingCount", { shown: filteredLearningObjectives.length, total: learningObjectives.length }))), /* @__PURE__ */ React169__default.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subject-filter" }, t4("settingsModal.topics.filterBySubject")), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedSubjectFilter, onValueChange: setSelectedSubjectFilter, disabled: subjects.length === 0 }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "subject-filter", className: "w-full sm:w-[280px]" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("settingsModal.topics.filterPlaceholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "all" }, t4("settingsModal.topics.allSubjects")), subjects.map((subject) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: subject, value: subject }, subject))))), /* @__PURE__ */ React169__default.createElement("div", { className: "border rounded-lg" }, /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-72" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, { className: "sticky top-0 bg-muted" }, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, { className: "w-[150px]" }, t4("settingsModal.topics.tableHeaders.subject")), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "w-[150px]" }, t4("settingsModal.topics.tableHeaders.category")), /* @__PURE__ */ React169__default.createElement(TableHead, null, t4("settingsModal.topics.tableHeaders.topic")), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "w-[100px]" }, t4("settingsModal.topics.tableHeaders.loId")))), /* @__PURE__ */ React169__default.createElement(TableBody, null, isLoading ? /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableCell, { colSpan: 4, className: "text-center" }, t4("common.loading"))) : filteredLearningObjectives.length > 0 ? filteredLearningObjectives.map((lo) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: lo.loId }, /* @__PURE__ */ React169__default.createElement(TableCell, null, lo.subject), /* @__PURE__ */ React169__default.createElement(TableCell, null, lo.category), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium" }, lo.topic), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, lo.loId))) : /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableCell, { colSpan: 4, className: "text-center h-24 text-muted-foreground" }, t4("settingsModal.topics.emptyData"))))))))), /* @__PURE__ */ React169__default.createElement(CardFooter, { className: "px-1" }, /* @__PURE__ */ React169__default.createElement(AlertDialog2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "destructive", disabled: learningObjectives.length === 0 }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "mr-2 h-4 w-4" }), t4("settingsModal.topics.clearAllData"))), /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, t4("settingsModal.topics.clearDataConfirmationTitle")), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, t4("settingsModal.topics.clearDataConfirmationMessage", { count: learningObjectives.length }))), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: handleClearData }, t4("settingsModal.topics.confirmDelete"))))))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isConfirmModalOpen, onOpenChange: setIsConfirmModalOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, t4("settingsModal.topics.confirmModal.title")), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, t4("settingsModal.topics.confirmModal.description", { count: parsedImportData?.data.length || 0 }))), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, { className: "gap-2" }, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { onClick: () => setParsedImportData(null) }, t4("common.cancel")), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: handleConfirmMerge, className: "bg-blue-600 hover:bg-blue-700" }, t4("settingsModal.topics.confirmModal.mergeButton")), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: handleConfirmOverwrite, className: "bg-amber-600 hover:bg-amber-700" }, t4("settingsModal.topics.confirmModal.overwriteButton"))))));
134712
+ } }, t4("settingsModal.topics.viewHeaderFormat"))), /* @__PURE__ */ React169__default.createElement(Input, { id: "tsv-importer", type: "file", ref: fileInputRef, accept: ".tsv,text/tab-separated-values", onChange: handleFileChange, className: "hidden" }), /* @__PURE__ */ React169__default.createElement(Button, { onClick: () => fileInputRef.current?.click() }, /* @__PURE__ */ React169__default.createElement(Upload, { className: "mr-2 h-4 w-4" }), t4("settingsModal.topics.chooseFile")))), importErrors.length > 0 && /* @__PURE__ */ React169__default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React169__default.createElement(CircleAlert, { className: "h-4 w-4" }), /* @__PURE__ */ React169__default.createElement(AlertTitle, null, t4("settingsModal.topics.importErrors")), /* @__PURE__ */ React169__default.createElement(AlertDescription, null, /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-24 mt-2" }, /* @__PURE__ */ React169__default.createElement("ul", { className: "list-disc list-inside text-xs space-y-1" }, importErrors.map((error, index3) => /* @__PURE__ */ React169__default.createElement("li", { key: index3 }, error)))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between mb-2" }, /* @__PURE__ */ React169__default.createElement("h3", { className: "text-lg font-semibold" }, t4("settingsModal.topics.currentDataTitle")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("settingsModal.topics.showingCount", { shown: filteredLearningObjectives.length, total: learningObjectives.length }))), /* @__PURE__ */ React169__default.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subject-filter" }, t4("settingsModal.topics.filterBySubject")), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedSubjectFilter, onValueChange: setSelectedSubjectFilter, disabled: subjects.length === 0 }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "subject-filter", className: "w-full sm:w-[280px]" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("settingsModal.topics.filterPlaceholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "all" }, t4("settingsModal.topics.allSubjects")), subjects.map((subject) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: subject, value: subject }, subject))))), /* @__PURE__ */ React169__default.createElement("div", { className: "border rounded-lg" }, /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-72" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, { className: "sticky top-0 bg-muted" }, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, { className: "w-[150px]" }, t4("settingsModal.topics.tableHeaders.subject")), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "w-[150px]" }, t4("settingsModal.topics.tableHeaders.category")), /* @__PURE__ */ React169__default.createElement(TableHead, null, t4("settingsModal.topics.tableHeaders.topic")), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "w-[100px]" }, t4("settingsModal.topics.tableHeaders.code")))), /* @__PURE__ */ React169__default.createElement(TableBody, null, isLoading ? /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableCell, { colSpan: 4, className: "text-center" }, t4("common.loading"))) : filteredLearningObjectives.length > 0 ? filteredLearningObjectives.map((lo) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: lo.code }, /* @__PURE__ */ React169__default.createElement(TableCell, null, lo.subject), /* @__PURE__ */ React169__default.createElement(TableCell, null, lo.category), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium" }, lo.topic), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, lo.code))) : /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableCell, { colSpan: 4, className: "text-center h-24 text-muted-foreground" }, t4("settingsModal.topics.emptyData"))))))))), /* @__PURE__ */ React169__default.createElement(CardFooter, { className: "px-1" }, /* @__PURE__ */ React169__default.createElement(AlertDialog2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "destructive", disabled: learningObjectives.length === 0 }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "mr-2 h-4 w-4" }), t4("settingsModal.topics.clearAllData"))), /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, t4("settingsModal.topics.clearDataConfirmationTitle")), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, t4("settingsModal.topics.clearDataConfirmationMessage", { count: learningObjectives.length }))), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: handleClearData }, t4("settingsModal.topics.confirmDelete"))))))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isConfirmModalOpen, onOpenChange: setIsConfirmModalOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, t4("settingsModal.topics.confirmModal.title")), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, t4("settingsModal.topics.confirmModal.description", { count: parsedImportData?.data.length || 0 }))), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, { className: "gap-2" }, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { onClick: () => setParsedImportData(null) }, t4("common.cancel")), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: handleConfirmMerge, className: "bg-blue-600 hover:bg-blue-700" }, t4("settingsModal.topics.confirmModal.mergeButton")), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: handleConfirmOverwrite, className: "bg-amber-600 hover:bg-amber-700" }, t4("settingsModal.topics.confirmModal.overwriteButton"))))));
134713
134713
  };
134714
134714
 
134715
134715
  // src/react-ui/components/app/ManageImageContexts.tsx
@@ -135116,7 +135116,7 @@ var SettingsModal = ({ isOpen, onClose, defaultTab = "personal" }) => {
135116
135116
  return goal.id;
135117
135117
  }
135118
135118
  };
135119
- return /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => !open && onClose() }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-xl md:max-w-2xl lg:max-w-3xl max-h-[85vh] flex flex-col" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, { className: "shrink-0" }, /* @__PURE__ */ React169__default.createElement(DialogTitle2, { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Settings, { className: "mr-2 h-5 w-5 text-primary" }), t4("settingsModal.title")), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, t4("settingsModal.description"))), /* @__PURE__ */ React169__default.createElement(Tabs2, { value: activeTab, onValueChange: (value) => setActiveTab(value), className: "pt-2 flex-1 flex flex-col min-h-0" }, /* @__PURE__ */ React169__default.createElement(TabsList2, { className: "grid w-full grid-cols-5 shrink-0" }, /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "personal" }, /* @__PURE__ */ React169__default.createElement(User, { className: "mr-1 h-4 w-4" }), t4("settingsModal.personalTab")), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "topics" }, /* @__PURE__ */ React169__default.createElement(ListTodo, { className: "mr-1 h-4 w-4" }), t4("settingsModal.topicsTab")), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "imageContexts" }, /* @__PURE__ */ React169__default.createElement(ImagePlus, { className: "mr-1 h-4 w-4" }), "Images"), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "layout" }, /* @__PURE__ */ React169__default.createElement(LayoutDashboard, { className: "mr-1 h-4 w-4" }), t4("settingsModal.layoutTab")), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "apiKeys" }, /* @__PURE__ */ React169__default.createElement(KeyRound, { className: "mr-1 h-4 w-4" }), t4("settingsModal.apiKeysTab"))), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "personal", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-full pr-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold mb-3" }, t4("settingsModal.personal.basicInfo")), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "full-name" }, t4("settingsModal.personal.fullName")), /* @__PURE__ */ React169__default.createElement(Input, { id: "full-name", value: fullName, onChange: (e3) => setFullName(e3.target.value), placeholder: t4("settingsModal.personal.fullNamePlaceholder") })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "weekly-goal" }, t4("settingsModal.personal.weeklyGoal")), /* @__PURE__ */ React169__default.createElement(Input, { id: "weekly-goal", type: "number", value: weeklyGoal, onChange: (e3) => setWeeklyGoal(parseInt(e3.target.value, 10) || 0), min: "1" })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "language-select", className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Languages, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.languageSelectLabel")), /* @__PURE__ */ React169__default.createElement(Select2, { value: language3, onValueChange: changeLanguage2 }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "language-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select a language..." })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "en" }, "English"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "vi" }, "Ti\u1EBFng Vi\u1EC7t")))))), /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold mb-3 flex items-center" }, /* @__PURE__ */ React169__default.createElement(Target, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.advancedGoals")), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2 mb-4" }, advancedGoals.map((goal) => /* @__PURE__ */ React169__default.createElement("div", { key: goal.id, className: "flex items-center justify-between p-2 bg-muted/50 rounded-md" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm flex-1" }, renderGoalDescription(goal)), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleDeleteGoal(goal.id) }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), advancedGoals.length === 0 && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("settingsModal.personal.noGoals"))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement("h5", { className: "font-medium" }, t4("settingsModal.personal.addNewGoal")), /* @__PURE__ */ React169__default.createElement(Select2, { value: newGoalType, onValueChange: (v) => setNewGoalType(v) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.goalTypePlaceholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "average_score_subject" }, t4("settingsModal.personal.goalType.avgScoreSubject")), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "mastery_topic" }, t4("settingsModal.personal.goalType.masteryTopic")))), newGoalType === "average_score_subject" && /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 animate-in fade-in" }, /* @__PURE__ */ React169__default.createElement(Select2, { value: newGoalSubject, onValueChange: setNewGoalSubject }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.subjectPlaceholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, allSubjects.map((s4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: s4, value: s4 }, s4)))), /* @__PURE__ */ React169__default.createElement(Input, { type: "number", value: newGoalTargetValue, onChange: (e3) => setNewGoalTargetValue(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.targetScorePlaceholder") })), newGoalType === "mastery_topic" && /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 animate-in fade-in" }, /* @__PURE__ */ React169__default.createElement(Select2, { value: newGoalSubject, onValueChange: setNewGoalSubject }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.subjectPlaceholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, allSubjects.map((s4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: s4, value: s4 }, s4)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: newGoalTopic, onValueChange: setNewGoalTopic, disabled: !newGoalSubject }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.topicPlaceholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, topicsForSelectedSubject.map((t5) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: t5, value: t5 }, t5)))), /* @__PURE__ */ React169__default.createElement(Input, { type: "number", value: newGoalTargetValue, onChange: (e3) => setNewGoalTargetValue(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.targetScorePlaceholder") }), /* @__PURE__ */ React169__default.createElement(Input, { type: "number", value: newGoalConsecutive, onChange: (e3) => setNewGoalConsecutive(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.consecutiveSessionsPlaceholder") })), newGoalType && /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", onClick: handleAddNewGoal }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.addGoalButton"))))))), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "topics", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__default.createElement(ManageTopics, null)), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "imageContexts", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__default.createElement(ManageImageContexts, null)), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "layout", className: "space-y-4 pt-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold" }, t4("settingsModal.layout.title")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground mt-1 mb-3" }, t4("settingsModal.layout.description")), /* @__PURE__ */ React169__default.createElement(AlertDialog2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "destructive" }, /* @__PURE__ */ React169__default.createElement(RefreshCw, { className: "mr-2 h-4 w-4" }), t4("settingsModal.layout.resetButton"))), /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, t4("settingsModal.layout.resetConfirmationTitle")), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, t4("settingsModal.layout.resetConfirmationMessage"))), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: handleResetLayout }, t4("settingsModal.layout.confirmReset"))))))), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "apiKeys", className: "space-y-4 pt-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "gemini-api-key" }, t4("settingsModal.apiKeys.geminiKey")), /* @__PURE__ */ React169__default.createElement(Input, { id: "gemini-api-key", type: "password", value: geminiApiKey, onChange: (e3) => setGeminiApiKey(e3.target.value), placeholder: t4("settingsModal.apiKeys.geminiKeyPlaceholder") }), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, t4("settingsModal.apiKeys.storageHint"))))), /* @__PURE__ */ React169__default.createElement(DialogFooter, { className: "gap-2 sm:justify-end pt-4 shrink-0" }, /* @__PURE__ */ React169__default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline" }, t4("common.close"))), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", onClick: handleSave }, /* @__PURE__ */ React169__default.createElement(Save, { className: "mr-2 h-4 w-4" }), t4("settingsModal.saveChanges")))));
135119
+ return /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => !open && onClose() }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-xl md:max-w-2xl lg:max-w-3xl max-h-[85vh] flex flex-col" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, { className: "shrink-0" }, /* @__PURE__ */ React169__default.createElement(DialogTitle2, { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Settings, { className: "mr-2 h-5 w-5 text-primary" }), t4("settingsModal.title")), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, t4("settingsModal.description"))), /* @__PURE__ */ React169__default.createElement(Tabs2, { value: activeTab, onValueChange: (value) => setActiveTab(value), className: "pt-2 flex-1 flex flex-col min-h-0" }, /* @__PURE__ */ React169__default.createElement(TabsList2, { className: "grid w-full grid-cols-5 shrink-0" }, /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "personal" }, /* @__PURE__ */ React169__default.createElement(User, { className: "mr-1 h-4 w-4" }), t4("settingsModal.personalTab")), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "topics" }, /* @__PURE__ */ React169__default.createElement(ListTodo, { className: "mr-1 h-4 w-4" }), t4("settingsModal.topicsTab")), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "imageContexts" }, /* @__PURE__ */ React169__default.createElement(ImagePlus, { className: "mr-1 h-4 w-4" }), "Images"), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "layout" }, /* @__PURE__ */ React169__default.createElement(LayoutDashboard, { className: "mr-1 h-4 w-4" }), t4("settingsModal.layoutTab")), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "apiKeys" }, /* @__PURE__ */ React169__default.createElement(KeyRound, { className: "mr-1 h-4 w-4" }), t4("settingsModal.apiKeysTab"))), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "personal", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-full pr-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold mb-3" }, t4("settingsModal.personal.basicInfo")), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "full-name" }, t4("settingsModal.personal.fullName")), /* @__PURE__ */ React169__default.createElement(Input, { id: "full-name", value: fullName, onChange: (e3) => setFullName(e3.target.value), placeholder: t4("settingsModal.personal.fullNamePlaceholder") })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "weekly-goal" }, t4("settingsModal.personal.weeklyGoal")), /* @__PURE__ */ React169__default.createElement(Input, { id: "weekly-goal", type: "number", value: weeklyGoal, onChange: (e3) => setWeeklyGoal(parseInt(e3.target.value, 10) || 0), min: "1" })), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "language-select", className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Languages, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.languageSelectLabel")), /* @__PURE__ */ React169__default.createElement(Select2, { value: language3, onValueChange: changeLanguage2 }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "language-select" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select a language..." })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "en" }, "English"), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "vi" }, "Ti\u1EBFng Vi\u1EC7t")))))), /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold mb-3 flex items-center" }, /* @__PURE__ */ React169__default.createElement(Target, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.advancedGoals")), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2 mb-4" }, advancedGoals.map((goal) => /* @__PURE__ */ React169__default.createElement("div", { key: goal.id, className: "flex items-center justify-between p-2 bg-muted/50 rounded-md" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm flex-1" }, renderGoalDescription(goal)), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleDeleteGoal(goal.id) }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), advancedGoals.length === 0 && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("settingsModal.personal.noGoals"))), /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__default.createElement("h5", { className: "font-Medium" }, t4("settingsModal.personal.addNewGoal")), /* @__PURE__ */ React169__default.createElement(Select2, { value: newGoalType, onValueChange: (v) => setNewGoalType(v) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.goalTypePlaceholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "average_score_subject" }, t4("settingsModal.personal.goalType.avgScoreSubject")), /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "mastery_topic" }, t4("settingsModal.personal.goalType.masteryTopic")))), newGoalType === "average_score_subject" && /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 animate-in fade-in" }, /* @__PURE__ */ React169__default.createElement(Select2, { value: newGoalSubject, onValueChange: setNewGoalSubject }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.subjectPlaceholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, allSubjects.map((s4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: s4, value: s4 }, s4)))), /* @__PURE__ */ React169__default.createElement(Input, { type: "number", value: newGoalTargetValue, onChange: (e3) => setNewGoalTargetValue(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.targetScorePlaceholder") })), newGoalType === "mastery_topic" && /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-2 gap-2 animate-in fade-in" }, /* @__PURE__ */ React169__default.createElement(Select2, { value: newGoalSubject, onValueChange: setNewGoalSubject }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.subjectPlaceholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, allSubjects.map((s4) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: s4, value: s4 }, s4)))), /* @__PURE__ */ React169__default.createElement(Select2, { value: newGoalTopic, onValueChange: setNewGoalTopic, disabled: !newGoalSubject }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.topicPlaceholder") })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, topicsForSelectedSubject.map((t5) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: t5, value: t5 }, t5)))), /* @__PURE__ */ React169__default.createElement(Input, { type: "number", value: newGoalTargetValue, onChange: (e3) => setNewGoalTargetValue(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.targetScorePlaceholder") }), /* @__PURE__ */ React169__default.createElement(Input, { type: "number", value: newGoalConsecutive, onChange: (e3) => setNewGoalConsecutive(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.consecutiveSessionsPlaceholder") })), newGoalType && /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", onClick: handleAddNewGoal }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.addGoalButton"))))))), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "topics", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__default.createElement(ManageTopics, null)), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "imageContexts", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__default.createElement(ManageImageContexts, null)), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "layout", className: "space-y-4 pt-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold" }, t4("settingsModal.layout.title")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground mt-1 mb-3" }, t4("settingsModal.layout.description")), /* @__PURE__ */ React169__default.createElement(AlertDialog2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "destructive" }, /* @__PURE__ */ React169__default.createElement(RefreshCw, { className: "mr-2 h-4 w-4" }), t4("settingsModal.layout.resetButton"))), /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, t4("settingsModal.layout.resetConfirmationTitle")), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, t4("settingsModal.layout.resetConfirmationMessage"))), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: handleResetLayout }, t4("settingsModal.layout.confirmReset"))))))), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "apiKeys", className: "space-y-4 pt-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "gemini-api-key" }, t4("settingsModal.apiKeys.geminiKey")), /* @__PURE__ */ React169__default.createElement(Input, { id: "gemini-api-key", type: "password", value: geminiApiKey, onChange: (e3) => setGeminiApiKey(e3.target.value), placeholder: t4("settingsModal.apiKeys.geminiKeyPlaceholder") }), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, t4("settingsModal.apiKeys.storageHint"))))), /* @__PURE__ */ React169__default.createElement(DialogFooter, { className: "gap-2 sm:justify-end pt-4 shrink-0" }, /* @__PURE__ */ React169__default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline" }, t4("common.close"))), /* @__PURE__ */ React169__default.createElement(Button, { type: "button", onClick: handleSave }, /* @__PURE__ */ React169__default.createElement(Save, { className: "mr-2 h-4 w-4" }), t4("settingsModal.saveChanges")))));
135120
135120
  };
135121
135121
 
135122
135122
  // src/react-ui/components/dashboard/Cheatsheet.tsx
@@ -135215,7 +135215,7 @@ var Cheatsheet = () => {
135215
135215
  }
135216
135216
  return null;
135217
135217
  };
135218
- return /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), t4("knowledgeCards.title")), /* @__PURE__ */ React169__default.createElement(CardDescription, null, t4("knowledgeCards.description"))), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React169__default.createElement(Input, { type: "search", placeholder: t4("knowledgeCards.searchPlaceholder"), value: searchQuery, onChange: (e3) => setSearchQuery2(e3.target.value), disabled: allCards.length === 0 }), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, allCards.length > 0 ? filteredCards.length > 0 ? /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, filteredCards.map((card) => /* @__PURE__ */ React169__default.createElement("button", { key: card.id, onClick: () => handleCardClick(card.id), className: "w-full text-left p-2 rounded-md hover:bg-accent text-sm font-medium" }, card.concept))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("knowledgeCards.noMatch"))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("knowledgeCards.empty")))), renderActionArea())), /* @__PURE__ */ React169__default.createElement(
135218
+ return /* @__PURE__ */ React169__default.createElement(React169__default.Fragment, null, /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), t4("knowledgeCards.title")), /* @__PURE__ */ React169__default.createElement(CardDescription, null, t4("knowledgeCards.description"))), /* @__PURE__ */ React169__default.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React169__default.createElement(Input, { type: "search", placeholder: t4("knowledgeCards.searchPlaceholder"), value: searchQuery, onChange: (e3) => setSearchQuery2(e3.target.value), disabled: allCards.length === 0 }), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, allCards.length > 0 ? filteredCards.length > 0 ? /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, filteredCards.map((card) => /* @__PURE__ */ React169__default.createElement("button", { key: card.id, onClick: () => handleCardClick(card.id), className: "w-full text-left p-2 rounded-md hover:bg-accent text-sm font-Medium" }, card.concept))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("knowledgeCards.noMatch"))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("knowledgeCards.empty")))), renderActionArea())), /* @__PURE__ */ React169__default.createElement(
135219
135219
  CardViewerDialog,
135220
135220
  {
135221
135221
  isOpen: isDialogOpen,
@@ -135242,9 +135242,9 @@ var StatCard = ({
135242
135242
  isLoading = false
135243
135243
  }) => {
135244
135244
  if (isLoading) {
135245
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-sm font-medium" }, title), /* @__PURE__ */ React169__default.createElement(Skeleton, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement(Skeleton, { className: "h-8 w-3/4 mb-2" }), /* @__PURE__ */ React169__default.createElement(Skeleton, { className: "h-4 w-1/2" })));
135245
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-sm font-Medium" }, title), /* @__PURE__ */ React169__default.createElement(Skeleton, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement(Skeleton, { className: "h-8 w-3/4 mb-2" }), /* @__PURE__ */ React169__default.createElement(Skeleton, { className: "h-4 w-1/2" })));
135246
135246
  }
135247
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-sm font-medium" }, title), /* @__PURE__ */ React169__default.createElement("div", { className: "text-muted-foreground" }, icon)), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement("div", { className: "text-2xl font-bold" }, value, unit2 && /* @__PURE__ */ React169__default.createElement("span", { className: "text-xl font-medium text-muted-foreground ml-1" }, unit2)), context && /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, context)));
135247
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "text-sm font-Medium" }, title), /* @__PURE__ */ React169__default.createElement("div", { className: "text-muted-foreground" }, icon)), /* @__PURE__ */ React169__default.createElement(CardContent, null, /* @__PURE__ */ React169__default.createElement("div", { className: "text-2xl font-bold" }, value, unit2 && /* @__PURE__ */ React169__default.createElement("span", { className: "text-xl font-Medium text-muted-foreground ml-1" }, unit2)), context && /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs text-muted-foreground" }, context)));
135248
135248
  };
135249
135249
 
135250
135250
  // src/react-ui/components/dashboard/PerformanceSnapshot.tsx
@@ -135343,7 +135343,7 @@ var RoadmapChecklist = () => {
135343
135343
  }, []);
135344
135344
  const handleStartPractice = useCallback((item) => {
135345
135345
  const practiceConfig = {
135346
- loIds: [item.loId],
135346
+ loIds: [item.code],
135347
135347
  difficulty: item.suggestedDifficulty,
135348
135348
  language: "Vietnamese"
135349
135349
  };
@@ -135353,7 +135353,7 @@ var RoadmapChecklist = () => {
135353
135353
  return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(CalendarCheck, { className: "mr-2 h-5 w-5 text-primary" }), t4("roadmap.title")), /* @__PURE__ */ React169__default.createElement(CardDescription, null, t4("roadmap.description"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, !roadmap || !roadmap.items || roadmap.items.length === 0 ? /* @__PURE__ */ React169__default.createElement("div", { className: "text-center text-muted-foreground py-8" }, /* @__PURE__ */ React169__default.createElement("p", null, t4("roadmap.emptyState")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm" }, t4("roadmap.emptyStateSuggestion"))) : /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, roadmap.items.map((item, index3) => /* @__PURE__ */ React169__default.createElement(
135354
135354
  "div",
135355
135355
  {
135356
- key: `${item.loId}-${index3}`,
135356
+ key: `${item.code}-${index3}`,
135357
135357
  className: "flex items-center justify-between p-3 border rounded-md bg-background hover:bg-muted/50 transition-colors"
135358
135358
  },
135359
135359
  /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-start gap-3" }, /* @__PURE__ */ React169__default.createElement(
@@ -136896,7 +136896,7 @@ function Calendar2({
136896
136896
  months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
136897
136897
  month: "space-y-4",
136898
136898
  caption: "flex justify-center pt-1 relative items-center",
136899
- caption_label: "text-sm font-medium",
136899
+ caption_label: "text-sm font-Medium",
136900
136900
  nav: "space-x-1 flex items-center",
136901
136901
  nav_button: cn(
136902
136902
  buttonVariants({ variant: "outline" }),
@@ -137018,7 +137018,7 @@ var AnalysisDialog = ({ isOpen, onClose }) => {
137018
137018
  }
137019
137019
  try {
137020
137020
  const allAvailableTopics = TopicDataService.getData().map((lo) => ({
137021
- loId: lo.loId,
137021
+ code: lo.code,
137022
137022
  subject: lo.subject,
137023
137023
  category: lo.category,
137024
137024
  topic: lo.topic
@@ -137471,7 +137471,7 @@ var GeneratedQuizzesCard = () => {
137471
137471
  onChange: (e3) => setSearchQuery2(e3.target.value),
137472
137472
  disabled: uniqueQuizzes.length === 0
137473
137473
  }
137474
- ), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, uniqueQuizzes.length > 0 ? filteredQuizzes.length > 0 ? /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, filteredQuizzes.map((quiz) => /* @__PURE__ */ React169__default.createElement("div", { key: quiz.id, className: "flex items-center justify-between p-2 rounded-md hover:bg-accent" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm font-medium truncate pr-2", title: quiz.title }, quiz.title), /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", variant: "ghost", onClick: () => handleRetake(quiz) }, /* @__PURE__ */ React169__default.createElement(CirclePlay, { className: "mr-2 h-4 w-4" }), t4("common.retake"))))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.generated.noMatch"))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.generated.empty"))))));
137474
+ ), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, uniqueQuizzes.length > 0 ? filteredQuizzes.length > 0 ? /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, filteredQuizzes.map((quiz) => /* @__PURE__ */ React169__default.createElement("div", { key: quiz.id, className: "flex items-center justify-between p-2 rounded-md hover:bg-accent" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm font-Medium truncate pr-2", title: quiz.title }, quiz.title), /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", variant: "ghost", onClick: () => handleRetake(quiz) }, /* @__PURE__ */ React169__default.createElement(CirclePlay, { className: "mr-2 h-4 w-4" }), t4("common.retake"))))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.generated.noMatch"))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.generated.empty"))))));
137475
137475
  };
137476
137476
 
137477
137477
  // src/react-ui/components/app/UploadResourceModal.tsx
@@ -137551,11 +137551,12 @@ var UploadResourceModal = ({ isOpen, onClose }) => {
137551
137551
  language: i18n.language === "vi" ? "Vietnamese" : "English",
137552
137552
  documentContent: textContent,
137553
137553
  learningObjectives: learningObjectives.map((lo) => ({
137554
- loId: lo.loId,
137554
+ name: lo.name,
137555
+ code: lo.code,
137555
137556
  subject: lo.subject,
137556
137557
  category: lo.category,
137557
137558
  topic: lo.topic,
137558
- loDescription: lo.loDescription
137559
+ description: lo.description || ""
137559
137560
  }))
137560
137561
  }, apiKey);
137561
137562
  setAnalysisResult(result);
@@ -137588,12 +137589,12 @@ var UploadResourceModal = ({ isOpen, onClose }) => {
137588
137589
  generatedQuestions = result.generatedQuestions;
137589
137590
  } else {
137590
137591
  const plan = analysisResult.mappedLOs.map((lo) => {
137591
- const sourceLO = TopicDataService.getData().find((orig) => orig.loId === lo.loId);
137592
+ const sourceLO = TopicDataService.getData().find((orig) => orig.code === lo.code);
137592
137593
  return {
137593
137594
  plannedTopic: lo.reasoning,
137594
137595
  plannedQuestionType: "multiple_choice",
137595
137596
  plannedBloomLevel: "understanding",
137596
- originalLoId: lo.loId,
137597
+ originalLoId: lo.code,
137597
137598
  originalTopic: sourceLO?.topic,
137598
137599
  originalCategory: sourceLO?.category,
137599
137600
  originalSubject: sourceLO?.subject
@@ -137638,7 +137639,7 @@ var UploadResourceModal = ({ isOpen, onClose }) => {
137638
137639
  return /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col items-center justify-center h-48" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-12 w-12 animate-spin text-primary" }), /* @__PURE__ */ React169__default.createElement("p", { className: "mt-4 text-muted-foreground font-semibold" }, stage === "analyzing" && t4("dialogs.uploadResource.analyzing"), stage === "generating" && t4("dialogs.uploadResource.generating")));
137639
137640
  case "result":
137640
137641
  if (!analysisResult) return null;
137641
- return /* @__PURE__ */ React169__default.createElement("div", { className: "py-4 space-y-4" }, /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardContent, { className: "p-4" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold mb-2" }, "AI Analysis Complete"), analysisResult.isFreestyleRecommended ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-start gap-3 text-amber-700" }, /* @__PURE__ */ React169__default.createElement(BrainCircuit, { className: "h-5 w-5 mt-1 flex-shrink-0" }), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "font-medium" }, t4("dialogs.uploadResource.freestyleRecommended")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm" }, t4("dialogs.uploadResource.freestyleDescription")))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-start gap-3 text-green-700" }, /* @__PURE__ */ React169__default.createElement(CircleCheckBig, { className: "h-5 w-5 mt-1 flex-shrink-0" }), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "font-medium" }, t4("dialogs.uploadResource.curriculumMatch")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm" }, t4("dialogs.uploadResource.curriculumDescription")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs mt-2 font-semibold" }, t4("dialogs.uploadResource.mappedTopics")), /* @__PURE__ */ React169__default.createElement("ul", { className: "list-disc list-inside text-xs" }, analysisResult.mappedLOs.map((lo) => /* @__PURE__ */ React169__default.createElement("li", { key: lo.loId }, TopicDataService.getData().find((orig) => orig.loId === lo.loId)?.topic || lo.loId))))))), error && /* @__PURE__ */ React169__default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React169__default.createElement(CircleAlert, { className: "h-4 w-4" }), /* @__PURE__ */ React169__default.createElement(AlertTitle, null, t4("common.error")), /* @__PURE__ */ React169__default.createElement(AlertDescription, null, error)));
137642
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "py-4 space-y-4" }, /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardContent, { className: "p-4" }, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold mb-2" }, "AI Analysis Complete"), analysisResult.isFreestyleRecommended ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-start gap-3 text-amber-700" }, /* @__PURE__ */ React169__default.createElement(BrainCircuit, { className: "h-5 w-5 mt-1 flex-shrink-0" }), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "font-Medium" }, t4("dialogs.uploadResource.freestyleRecommended")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm" }, t4("dialogs.uploadResource.freestyleDescription")))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-start gap-3 text-green-700" }, /* @__PURE__ */ React169__default.createElement(CircleCheckBig, { className: "h-5 w-5 mt-1 flex-shrink-0" }), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("p", { className: "font-Medium" }, t4("dialogs.uploadResource.curriculumMatch")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm" }, t4("dialogs.uploadResource.curriculumDescription")), /* @__PURE__ */ React169__default.createElement("p", { className: "text-xs mt-2 font-semibold" }, t4("dialogs.uploadResource.mappedTopics")), /* @__PURE__ */ React169__default.createElement("ul", { className: "list-disc list-inside text-xs" }, analysisResult.mappedLOs.map((lo) => /* @__PURE__ */ React169__default.createElement("li", { key: lo.code }, TopicDataService.getData().find((orig) => orig.code === lo.code)?.topic || lo.code))))))), error && /* @__PURE__ */ React169__default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React169__default.createElement(CircleAlert, { className: "h-4 w-4" }), /* @__PURE__ */ React169__default.createElement(AlertTitle, null, t4("common.error")), /* @__PURE__ */ React169__default.createElement(AlertDescription, null, error)));
137642
137643
  }
137643
137644
  };
137644
137645
  return /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => !open && onClose() }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-lg" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(FileText, { className: "mr-2 h-5 w-5" }), t4("dialogs.uploadResource.title")), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, t4("dialogs.uploadResource.description"))), renderContent3(), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "outline" }, t4("common.cancel"))), stage === "result" && /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleGenerateQuiz }, /* @__PURE__ */ React169__default.createElement(Sparkles, { className: "mr-2 h-4 w-4" }), t4("dialogs.uploadResource.generateButton")))));
@@ -137759,7 +137760,7 @@ var FreestyleQuizzesCard = () => {
137759
137760
  onChange: (e3) => setSearchQuery2(e3.target.value),
137760
137761
  disabled: uniqueQuizzes.length === 0
137761
137762
  }
137762
- ), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, uniqueQuizzes.length > 0 ? filteredQuizzes.length > 0 ? /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, filteredQuizzes.map((quiz) => /* @__PURE__ */ React169__default.createElement("div", { key: quiz.id, className: "flex items-center justify-between p-2 rounded-md hover:bg-accent" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm font-medium truncate pr-2", title: quiz.title }, quiz.title), /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", variant: "ghost", onClick: () => handleRetake(quiz) }, /* @__PURE__ */ React169__default.createElement(CirclePlay, { className: "mr-2 h-4 w-4" }), t4("common.retake"))))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.freestyle.noMatch"))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.freestyle.empty"))))));
137763
+ ), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, uniqueQuizzes.length > 0 ? filteredQuizzes.length > 0 ? /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-1" }, filteredQuizzes.map((quiz) => /* @__PURE__ */ React169__default.createElement("div", { key: quiz.id, className: "flex items-center justify-between p-2 rounded-md hover:bg-accent" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm font-Medium truncate pr-2", title: quiz.title }, quiz.title), /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", variant: "ghost", onClick: () => handleRetake(quiz) }, /* @__PURE__ */ React169__default.createElement(CirclePlay, { className: "mr-2 h-4 w-4" }), t4("common.retake"))))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.freestyle.noMatch"))) : /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.freestyle.empty"))))));
137763
137764
  };
137764
137765
 
137765
137766
  // src/react-ui/components/common/ClientTranslation.tsx
@@ -137909,7 +137910,7 @@ var PersonalPracticeDashboard = ({ settingsPath, initialHistory, initialStats, i
137909
137910
  }
137910
137911
  try {
137911
137912
  const allAvailableTopics = TopicDataService.getData().map((lo) => ({
137912
- loId: lo.loId,
137913
+ code: lo.code,
137913
137914
  subject: lo.subject,
137914
137915
  category: lo.category,
137915
137916
  topic: lo.topic
@@ -138403,7 +138404,7 @@ var PracticeModeController = () => {
138403
138404
  try {
138404
138405
  const config3 = JSON.parse(suggestedConfigString);
138405
138406
  const allLOs = TopicDataService.getData();
138406
- const suggestedLOs = allLOs.filter((lo) => config3.loIds?.includes(lo.loId));
138407
+ const suggestedLOs = allLOs.filter((lo) => config3.loIds?.includes(lo.code));
138407
138408
  if (suggestedLOs.length > 0) {
138408
138409
  setInitialSuggestedLOs(suggestedLOs);
138409
138410
  setInitialSuggestedDifficulty(config3.difficulty || "Medium");
@@ -138446,9 +138447,10 @@ var PracticeModeController = () => {
138446
138447
  totalQuestions,
138447
138448
  numCodingQuestions,
138448
138449
  topics: selectedLOs.map((lo) => ({
138449
- topic: lo.loDescription,
138450
+ topic: lo.name || lo.code,
138451
+ // FIX: Provide fallback for name
138450
138452
  ratio: 100 / selectedLOs.length,
138451
- originalLoId: lo.loId,
138453
+ originalLoId: lo.code,
138452
138454
  originalSubject: lo.subject,
138453
138455
  originalCategory: lo.category,
138454
138456
  originalTopic: lo.topic
@@ -138640,7 +138642,7 @@ var SuggestionDialog = ({
138640
138642
  if (!suggestion) {
138641
138643
  return /* @__PURE__ */ React169__default.createElement("div", { className: "flex flex-col items-center justify-center h-64 gap-4" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-muted-foreground text-center" }, "Kh\xF4ng th\u1EC3 t\u1EA1o g\u1EE3i \xFD v\xE0o l\xFAc n\xE0y. Vui l\xF2ng th\u1EED l\u1EA1i."));
138642
138644
  }
138643
- return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 bg-muted/50 rounded-lg" }, /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: suggestion.suggestionText })), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold mb-3" }, "K\u1EBF ho\u1EA1ch Luy\u1EC7n t\u1EADp \u0111\u01B0\u1EE3c G\u1EE3i \xFD:"), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "max-h-[200px] pr-3" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, suggestion.suggestedTopics.map((topic) => /* @__PURE__ */ React169__default.createElement("div", { key: topic.loId, className: "flex items-center justify-between p-3 border rounded-md" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex-1 mr-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2 mb-1" }, topic.reason === "review" ? /* @__PURE__ */ React169__default.createElement(Badge2, { variant: "destructive" }, /* @__PURE__ */ React169__default.createElement(RefreshCw, { className: "h-3 w-3 mr-1.5" }), "\xD4n t\u1EADp") : /* @__PURE__ */ React169__default.createElement(Badge2, { variant: "secondary" }, /* @__PURE__ */ React169__default.createElement(Search, { className: "h-3 w-3 mr-1.5" }), "Kh\xE1m ph\xE1")), /* @__PURE__ */ React169__default.createElement("p", { className: "font-medium" }, topic.topicName)), /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", onClick: () => onStartSuggestedPractice(topic) }, /* @__PURE__ */ React169__default.createElement(CirclePlay, { className: "h-4 w-4 mr-2" }), "B\u1EAFt \u0111\u1EA7u")))))));
138645
+ return /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React169__default.createElement("div", { className: "p-4 bg-muted/50 rounded-lg" }, /* @__PURE__ */ React169__default.createElement(MarkdownRenderer, { content: suggestion.suggestionText })), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement("h4", { className: "font-semibold mb-3" }, "K\u1EBF ho\u1EA1ch Luy\u1EC7n t\u1EADp \u0111\u01B0\u1EE3c G\u1EE3i \xFD:"), /* @__PURE__ */ React169__default.createElement(ScrollArea2, { className: "max-h-[200px] pr-3" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, suggestion.suggestedTopics.map((topic) => /* @__PURE__ */ React169__default.createElement("div", { key: topic.code, className: "flex items-center justify-between p-3 border rounded-md" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex-1 mr-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2 mb-1" }, topic.reason === "review" ? /* @__PURE__ */ React169__default.createElement(Badge2, { variant: "destructive" }, /* @__PURE__ */ React169__default.createElement(RefreshCw, { className: "h-3 w-3 mr-1.5" }), "\xD4n t\u1EADp") : /* @__PURE__ */ React169__default.createElement(Badge2, { variant: "secondary" }, /* @__PURE__ */ React169__default.createElement(Search, { className: "h-3 w-3 mr-1.5" }), "Kh\xE1m ph\xE1")), /* @__PURE__ */ React169__default.createElement("p", { className: "font-Medium" }, topic.topicName)), /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", onClick: () => onStartSuggestedPractice(topic) }, /* @__PURE__ */ React169__default.createElement(CirclePlay, { className: "h-4 w-4 mr-2" }), "B\u1EAFt \u0111\u1EA7u")))))));
138644
138646
  };
138645
138647
  return /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => !open && onClose() }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-lg md:max-w-xl" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, { className: "flex items-center text-2xl" }, /* @__PURE__ */ React169__default.createElement(Lightbulb, { className: "h-6 w-6 mr-2 text-yellow-500" }), "G\u1EE3i \xFD Luy\u1EC7n t\u1EADp t\u1EEB AI"), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, "D\u1EF1a tr\xEAn k\u1EBFt qu\u1EA3 g\u1EA7n \u0111\xE2y, \u0111\xE2y l\xE0 nh\u1EEFng g\xEC gia s\u01B0 AI \u0111\u1EC1 xu\u1EA5t cho b\u1EA1n.")), /* @__PURE__ */ React169__default.createElement("div", { className: "py-4" }, renderContent3()), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline" }, "\u0110\xF3ng")))));
138646
138648
  };
@@ -138650,12 +138652,96 @@ init_react_shim();
138650
138652
 
138651
138653
  // src/react-ui/components/metadata/SubjectManager.tsx
138652
138654
  init_react_shim();
138655
+
138656
+ // src/react-ui/components/metadata/MetadataImportControls.tsx
138657
+ init_react_shim();
138658
+ function MetadataImportControls({ metadataName, onImport }) {
138659
+ const [isOpen, setIsOpen] = useState(false);
138660
+ const [jsonString, setJsonString] = useState("");
138661
+ const [isImporting, startImportTransition] = useTransition();
138662
+ const fileInputRef = useRef(null);
138663
+ const { toast: toast2 } = useToast();
138664
+ const processAndImportRecords = async (records, importSource) => {
138665
+ if (records.length === 0) {
138666
+ toast2({ title: "No Data", description: `The selected ${importSource === "file" ? "file" : "JSON string"} contains no data to import.`, variant: "destructive" });
138667
+ return;
138668
+ }
138669
+ await onImport(records);
138670
+ setIsOpen(false);
138671
+ setJsonString("");
138672
+ };
138673
+ const handleFileSelected = (event) => {
138674
+ const file = event.target.files?.[0];
138675
+ if (!file) return;
138676
+ if (file.type !== "application/json" && !file.name.endsWith(".csv")) {
138677
+ toast2({ title: "Invalid File Type", description: "Please select a JSON or CSV file.", variant: "destructive" });
138678
+ if (event.target) event.target.value = "";
138679
+ return;
138680
+ }
138681
+ startImportTransition(async () => {
138682
+ try {
138683
+ const fileContent = await file.text();
138684
+ let records = [];
138685
+ if (file.type === "application/json") {
138686
+ records = JSON.parse(fileContent);
138687
+ if (!Array.isArray(records)) throw new Error("JSON file must contain an array of objects.");
138688
+ } else if (file.name.endsWith(".csv")) {
138689
+ const lines = fileContent.split(/\r\n|\n/).filter((line) => line.trim() !== "");
138690
+ if (lines.length < 2) throw new Error("CSV must have a header and at least one data row.");
138691
+ const headers = lines[0].split(",").map((h3) => h3.trim());
138692
+ records = lines.slice(1).map((line) => {
138693
+ const values = line.split(",").map((v) => v.trim());
138694
+ const record = {};
138695
+ headers.forEach((header, index3) => {
138696
+ record[header] = values[index3];
138697
+ });
138698
+ return record;
138699
+ });
138700
+ }
138701
+ await processAndImportRecords(records, "file");
138702
+ } catch (err) {
138703
+ toast2({ title: "Import Error", description: `Failed to process file: ${err.message}`, variant: "destructive" });
138704
+ }
138705
+ });
138706
+ if (event.target) event.target.value = "";
138707
+ };
138708
+ const handleJsonStringImport = () => {
138709
+ if (!jsonString.trim()) {
138710
+ toast2({ title: "Missing Data", description: "JSON string cannot be empty.", variant: "destructive" });
138711
+ return;
138712
+ }
138713
+ startImportTransition(async () => {
138714
+ try {
138715
+ const records = JSON.parse(jsonString);
138716
+ if (!Array.isArray(records)) throw new Error("JSON string must represent an array of objects.");
138717
+ await processAndImportRecords(records, "text");
138718
+ } catch (err) {
138719
+ toast2({ title: "Import Error", description: `Failed to process JSON string: ${err.message}`, variant: "destructive" });
138720
+ }
138721
+ });
138722
+ };
138723
+ return /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React169__default.createElement(DialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", variant: "outline" }, /* @__PURE__ */ React169__default.createElement(Upload, { className: "mr-2 h-4 w-4" }), " Import ", metadataName)), /* @__PURE__ */ React169__default.createElement(DialogContent2, null, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, "Bulk Import ", metadataName), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, "Import multiple records from a file or by pasting JSON data.")), /* @__PURE__ */ React169__default.createElement(Tabs2, { defaultValue: "file" }, /* @__PURE__ */ React169__default.createElement(TabsList2, { className: "grid w-full grid-cols-2" }, /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "file" }, /* @__PURE__ */ React169__default.createElement(FileJson, { className: "mr-2 h-4 w-4" }), " From File"), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "text" }, /* @__PURE__ */ React169__default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), " From Text")), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "file", className: "pt-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, "Select a JSON or CSV file. Ensure column names in the file match the table schema (e.g., 'code', 'name', 'subject_code')."), /* @__PURE__ */ React169__default.createElement(Button, { variant: "outline", onClick: () => fileInputRef.current?.click(), disabled: isImporting }, isImporting ? /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__default.createElement(Upload, { className: "mr-2 h-4 w-4" }), isImporting ? "Importing..." : "Select File"), /* @__PURE__ */ React169__default.createElement("input", { type: "file", ref: fileInputRef, onChange: handleFileSelected, accept: ".json,.csv", className: "hidden" }))), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "text", className: "pt-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "jsonInput" }, "JSON Data (Array of Objects)"), /* @__PURE__ */ React169__default.createElement(
138724
+ Textarea,
138725
+ {
138726
+ id: "jsonInput",
138727
+ value: jsonString,
138728
+ onChange: (e3) => setJsonString(e3.target.value),
138729
+ placeholder: '[{"code": "SUB1", "name": "Subject 1"}, ...]',
138730
+ rows: 8,
138731
+ className: "font-mono text-xs",
138732
+ disabled: isImporting
138733
+ }
138734
+ ), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleJsonStringImport, disabled: isImporting || !jsonString.trim() }, isImporting ? /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), "Import from Text"))))));
138735
+ }
138736
+
138737
+ // src/react-ui/components/metadata/SubjectManager.tsx
138653
138738
  function SubjectManager({
138654
138739
  initialData,
138655
138740
  isLoading: isLoadingProp,
138656
138741
  onAdd,
138657
138742
  onUpdate,
138658
- onDelete
138743
+ onDelete,
138744
+ onBulkAdd
138659
138745
  }) {
138660
138746
  const [subjects, setSubjects] = useState([]);
138661
138747
  const [isLoading, setIsLoading] = useState(true);
@@ -138753,12 +138839,37 @@ function SubjectManager({
138753
138839
  }
138754
138840
  });
138755
138841
  };
138756
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React169__default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "subjectCode", value: subjectCode, onChange: (e3) => setSubjectCode(e3.target.value.toUpperCase()), placeholder: "e.g., MATH", disabled: !!currentSubject })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "subjectName", value: subjectName, onChange: (e3) => setSubjectName(e3.target.value), placeholder: "e.g., Mathematics" }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
138842
+ const handleImport = async (records) => {
138843
+ if (!onBulkAdd) return;
138844
+ const validatedRecords = records.map((rec) => {
138845
+ if (typeof rec.code === "string" && typeof rec.name === "string") {
138846
+ return { code: rec.code, name: rec.name };
138847
+ }
138848
+ return null;
138849
+ }).filter((rec) => rec !== null);
138850
+ if (validatedRecords.length !== records.length) {
138851
+ toast2({
138852
+ title: "Import Warning",
138853
+ description: "Some records had invalid or missing 'code' or 'name' fields and were ignored.",
138854
+ variant: "destructive"
138855
+ });
138856
+ }
138857
+ if (validatedRecords.length > 0) {
138858
+ await onBulkAdd(validatedRecords);
138859
+ }
138860
+ };
138861
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__default.createElement(
138862
+ MetadataImportControls,
138863
+ {
138864
+ metadataName: "Subjects",
138865
+ onImport: handleImport
138866
+ }
138867
+ ), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject")))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium" }, subject.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React169__default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "subjectCode", value: subjectCode, onChange: (e3) => setSubjectCode(e3.target.value.toUpperCase()), placeholder: "e.g., MATH", disabled: !!currentSubject })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "subjectName", value: subjectName, onChange: (e3) => setSubjectName(e3.target.value), placeholder: "e.g., Mathematics" }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
138757
138868
  }
138758
138869
 
138759
138870
  // src/react-ui/components/metadata/GradeLevelManager.tsx
138760
138871
  init_react_shim();
138761
- function GradeLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
138872
+ function GradeLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
138762
138873
  const [items, setItems] = useState([]);
138763
138874
  const [isLoading, setIsLoading] = useState(true);
138764
138875
  const [isDialogOpen, setIsDialogOpen] = useState(false);
@@ -138855,7 +138966,26 @@ function GradeLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpd
138855
138966
  }
138856
138967
  });
138857
138968
  };
138858
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Award, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Grade Levels"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Grade Level"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No grade levels found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Grade Level" : "Add New Grade Level")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., G9", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Grade 9" }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
138969
+ const handleImport = async (records) => {
138970
+ if (!onBulkAdd) return;
138971
+ const validatedRecords = records.map((rec) => {
138972
+ if (typeof rec.code === "string" && typeof rec.name === "string") {
138973
+ return { code: rec.code, name: rec.name };
138974
+ }
138975
+ return null;
138976
+ }).filter((rec) => rec !== null);
138977
+ if (validatedRecords.length !== records.length) {
138978
+ toast2({
138979
+ title: "Import Warning",
138980
+ description: "Some records had invalid or missing 'code' or 'name' fields and were ignored.",
138981
+ variant: "destructive"
138982
+ });
138983
+ }
138984
+ if (validatedRecords.length > 0) {
138985
+ await onBulkAdd(validatedRecords);
138986
+ }
138987
+ };
138988
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Award, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Grade Levels"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__default.createElement(MetadataImportControls, { metadataName: "Grade Levels", onImport: handleImport }), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Grade Level")))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No grade levels found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Grade Level" : "Add New Grade Level")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., G9", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Grade 9" }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
138859
138989
  }
138860
138990
 
138861
138991
  // src/react-ui/components/metadata/TopicManager.tsx
@@ -138866,7 +138996,8 @@ function TopicManager({
138866
138996
  isLoading: isLoadingProp,
138867
138997
  onAdd,
138868
138998
  onUpdate,
138869
- onDelete
138999
+ onDelete,
139000
+ onBulkAdd
138870
139001
  }) {
138871
139002
  const [topics, setTopics] = useState([]);
138872
139003
  const [subjects, setSubjects] = useState([]);
@@ -138970,15 +139101,34 @@ function TopicManager({
138970
139101
  }
138971
139102
  });
138972
139103
  };
139104
+ const handleImport = async (records) => {
139105
+ if (!onBulkAdd) return;
139106
+ const validatedRecords = records.map((rec) => {
139107
+ if (typeof rec.code === "string" && typeof rec.name === "string" && typeof rec.subjectCode === "string") {
139108
+ return { code: rec.code, name: rec.name, subjectCode: rec.subjectCode };
139109
+ }
139110
+ return null;
139111
+ }).filter((rec) => rec !== null);
139112
+ if (validatedRecords.length !== records.length) {
139113
+ toast2({
139114
+ title: "Import Warning",
139115
+ description: "Some records had invalid or missing 'code', 'name', or 'subjectCode' fields and were ignored.",
139116
+ variant: "destructive"
139117
+ });
139118
+ }
139119
+ if (validatedRecords.length > 0) {
139120
+ await onBulkAdd(validatedRecords);
139121
+ }
139122
+ };
138973
139123
  const getSubjectName = (subjectCode) => {
138974
139124
  return subjects.find((s4) => s4.code === subjectCode)?.name || "N/A";
138975
139125
  };
138976
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Tag, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Topics"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm", disabled: subjects.length === 0 }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic")), subjects.length === 0 && !isLoading && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-destructive" }, "Please add subjects before adding topics.")), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : topics.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No topics found. Add one to get started!") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, topics.map((topic) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: topic.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, topic.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium" }, topic.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, getSubjectName(topic.subjectCode), " (", topic.subjectCode, ")"), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(topic), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(topic), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Topic" : "Add New Topic")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Topic Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., ALG-BASICS", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Topic Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Algebra Basics" })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject"), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedSubjectCode, onValueChange: setSelectedSubjectCode, disabled: subjects.length === 0 }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "subjectCode" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select a subject" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, subjects.map((subject) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: subject.code, value: subject.code }, subject.name, " (", subject.code, ")")))))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() || !selectedSubjectCode }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete topic "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139126
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Tag, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Topics"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__default.createElement(MetadataImportControls, { metadataName: "Topics", onImport: handleImport }), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm", disabled: subjects.length === 0 }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic"))), subjects.length === 0 && !isLoading && /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-destructive" }, "Please add subjects before adding topics.")), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : topics.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No topics found. Add one to get started!") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, topics.map((topic) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: topic.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, topic.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium" }, topic.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, getSubjectName(topic.subjectCode), " (", topic.subjectCode, ")"), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(topic), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(topic), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Topic" : "Add New Topic")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Topic Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., ALG-BASICS", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Topic Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Algebra Basics" })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject"), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedSubjectCode, onValueChange: setSelectedSubjectCode, disabled: subjects.length === 0 }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "subjectCode" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select a subject" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, subjects.map((subject) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: subject.code, value: subject.code }, subject.name, " (", subject.code, ")")))))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() || !selectedSubjectCode }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete topic "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
138977
139127
  }
138978
139128
 
138979
139129
  // src/react-ui/components/metadata/CategoryManager.tsx
138980
139130
  init_react_shim();
138981
- function CategoryManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
139131
+ function CategoryManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
138982
139132
  const [items, setItems] = useState([]);
138983
139133
  const [isLoading, setIsLoading] = useState(true);
138984
139134
  const [isDialogOpen, setIsDialogOpen] = useState(false);
@@ -139078,12 +139228,37 @@ function CategoryManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdat
139078
139228
  }
139079
139229
  });
139080
139230
  };
139081
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Layers, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Categories"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Category"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No categories found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Category" : "Add New Category")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., CORE_CONCEPT", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Core Concept" })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Fundamental ideas within a subject." }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139231
+ const handleImport = async (records) => {
139232
+ if (!onBulkAdd) return;
139233
+ const validationResult = records.reduce((acc, rec) => {
139234
+ if (typeof rec.code === "string" && rec.code.trim() && typeof rec.name === "string" && rec.name.trim()) {
139235
+ acc.valid.push({
139236
+ code: rec.code,
139237
+ name: rec.name,
139238
+ description: typeof rec.description === "string" ? rec.description : void 0
139239
+ });
139240
+ } else {
139241
+ acc.invalidCount++;
139242
+ }
139243
+ return acc;
139244
+ }, { valid: [], invalidCount: 0 });
139245
+ if (validationResult.invalidCount > 0) {
139246
+ toast2({
139247
+ title: "Import Warning",
139248
+ description: `${validationResult.invalidCount} records had invalid or missing 'code' or 'name' fields and were ignored.`,
139249
+ variant: "destructive"
139250
+ });
139251
+ }
139252
+ if (validationResult.valid.length > 0) {
139253
+ await onBulkAdd(validationResult.valid);
139254
+ }
139255
+ };
139256
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Layers, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Categories"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__default.createElement(MetadataImportControls, { metadataName: "Categories", onImport: handleImport }), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Category")))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No categories found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Category" : "Add New Category")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., CORE_CONCEPT", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Core Concept" })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Fundamental ideas within a subject." }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139082
139257
  }
139083
139258
 
139084
139259
  // src/react-ui/components/metadata/BloomLevelManager.tsx
139085
139260
  init_react_shim();
139086
- function BloomLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
139261
+ function BloomLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
139087
139262
  const [items, setItems] = useState([]);
139088
139263
  const [isLoading, setIsLoading] = useState(true);
139089
139264
  const [isDialogOpen, setIsDialogOpen] = useState(false);
@@ -139183,12 +139358,37 @@ function BloomLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpd
139183
139358
  }
139184
139359
  });
139185
139360
  };
139186
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Brain, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Bloom's Levels"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Bloom's Level"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Bloom's Levels found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Bloom's Level" : "Add New Bloom's Level")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., REMEMBER", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Remembering" })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Recall facts and basic concepts." }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139361
+ const handleImport = async (records) => {
139362
+ if (!onBulkAdd) return;
139363
+ const validationResult = records.reduce((acc, rec) => {
139364
+ if (typeof rec.code === "string" && rec.code.trim() && typeof rec.name === "string" && rec.name.trim()) {
139365
+ acc.valid.push({
139366
+ code: rec.code,
139367
+ name: rec.name,
139368
+ description: typeof rec.description === "string" ? rec.description : void 0
139369
+ });
139370
+ } else {
139371
+ acc.invalidCount++;
139372
+ }
139373
+ return acc;
139374
+ }, { valid: [], invalidCount: 0 });
139375
+ if (validationResult.invalidCount > 0) {
139376
+ toast2({
139377
+ title: "Import Warning",
139378
+ description: `${validationResult.invalidCount} records had invalid or missing 'code' or 'name' fields and were ignored.`,
139379
+ variant: "destructive"
139380
+ });
139381
+ }
139382
+ if (validationResult.valid.length > 0) {
139383
+ await onBulkAdd(validationResult.valid);
139384
+ }
139385
+ };
139386
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Brain, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Bloom's Levels"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__default.createElement(MetadataImportControls, { metadataName: "Bloom's Levels", onImport: handleImport }), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Bloom's Level")))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Bloom's Levels found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Bloom's Level" : "Add New Bloom's Level")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., REMEMBER", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Remembering" })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Recall facts and basic concepts." }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139187
139387
  }
139188
139388
 
139189
139389
  // src/react-ui/components/metadata/QuestionTypeManager.tsx
139190
139390
  init_react_shim();
139191
- function QuestionTypeManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
139391
+ function QuestionTypeManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
139192
139392
  const [items, setItems] = useState([]);
139193
139393
  const [isLoading, setIsLoading] = useState(true);
139194
139394
  const [isDialogOpen, setIsDialogOpen] = useState(false);
@@ -139258,6 +139458,31 @@ function QuestionTypeManager({ initialData, isLoading: isLoadingProp, onAdd, onU
139258
139458
  }
139259
139459
  });
139260
139460
  };
139461
+ const handleImport = async (records) => {
139462
+ if (!onBulkAdd) return;
139463
+ const validationResult = records.reduce((acc, rec) => {
139464
+ if (typeof rec.code === "string" && rec.code.trim() && typeof rec.name === "string" && rec.name.trim()) {
139465
+ acc.valid.push({
139466
+ code: rec.code,
139467
+ name: rec.name,
139468
+ description: typeof rec.description === "string" ? rec.description : void 0
139469
+ });
139470
+ } else {
139471
+ acc.invalidCount++;
139472
+ }
139473
+ return acc;
139474
+ }, { valid: [], invalidCount: 0 });
139475
+ if (validationResult.invalidCount > 0) {
139476
+ toast2({
139477
+ title: "Import Warning",
139478
+ description: `${validationResult.invalidCount} records had invalid or missing 'code' or 'name' fields and were ignored.`,
139479
+ variant: "destructive"
139480
+ });
139481
+ }
139482
+ if (validationResult.valid.length > 0) {
139483
+ await onBulkAdd(validationResult.valid);
139484
+ }
139485
+ };
139261
139486
  const handleSubmit = () => {
139262
139487
  if (!itemName.trim() || !itemCode.trim()) {
139263
139488
  toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
@@ -139288,22 +139513,19 @@ function QuestionTypeManager({ initialData, isLoading: isLoadingProp, onAdd, onU
139288
139513
  }
139289
139514
  });
139290
139515
  };
139291
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(CircleHelp, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Question Types"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Question Type"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Question Types found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Question Type" : "Add New Question Type")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toLowerCase().replace(/ /g, "_")), placeholder: "e.g., multiple_choice", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Multiple Choice" })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Select one answer from a list of options." }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139516
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(CircleHelp, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Question Types"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__default.createElement(MetadataImportControls, { metadataName: "Question Types", onImport: handleImport }), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Question Type")))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Question Types found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Question Type" : "Add New Question Type")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toLowerCase().replace(/ /g, "_")), placeholder: "e.g., multiple_choice", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Multiple Choice" })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Select one answer from a list of options." }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139292
139517
  }
139293
139518
 
139294
139519
  // src/react-ui/components/metadata/LearningObjectiveManager.tsx
139295
139520
  init_react_shim();
139296
- function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
139521
+ function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
139297
139522
  const [items, setItems] = useState([]);
139298
139523
  const [subjects, setSubjects] = useState([]);
139299
139524
  const [isLoading, setIsLoading] = useState(true);
139300
139525
  const [isDialogOpen, setIsDialogOpen] = useState(false);
139301
139526
  const [isAlertOpen, setIsAlertOpen] = useState(false);
139302
139527
  const [currentItem, setCurrentItem] = useState(null);
139303
- const [itemName, setItemName] = useState("");
139304
- const [itemCode, setItemCode] = useState("");
139305
- const [itemDescription, setItemDescription] = useState("");
139306
- const [selectedSubjectCode, setSelectedSubjectCode] = useState(void 0);
139528
+ const [formState, setFormState] = useState({});
139307
139529
  const [itemToDelete, setItemToDelete] = useState(null);
139308
139530
  const [isPending, startTransition] = useTransition();
139309
139531
  const { toast: toast2 } = useToast();
@@ -139330,20 +139552,17 @@ function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoadi
139330
139552
  refreshData();
139331
139553
  }
139332
139554
  }, [isControlled, initialData, subjectsProp, isLoadingProp]);
139555
+ const handleFormChange = (field, value) => {
139556
+ setFormState((prev) => ({ ...prev, [field]: value }));
139557
+ };
139333
139558
  const handleAddItem = () => {
139334
139559
  setCurrentItem(null);
139335
- setItemName("");
139336
- setItemCode("");
139337
- setItemDescription("");
139338
- setSelectedSubjectCode(subjects.length > 0 ? subjects[0].code : void 0);
139560
+ setFormState({ subjectCode: subjects.length > 0 ? subjects[0].code : "" });
139339
139561
  setIsDialogOpen(true);
139340
139562
  };
139341
139563
  const handleEditItem = (item) => {
139342
139564
  setCurrentItem(item);
139343
- setItemName(item.name);
139344
- setItemCode(item.code);
139345
- setItemDescription(item.description || "");
139346
- setSelectedSubjectCode(item.subjectCode);
139565
+ setFormState(item);
139347
139566
  setIsDialogOpen(true);
139348
139567
  };
139349
139568
  const handleDeleteItem = (item) => {
@@ -139370,7 +139589,7 @@ function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoadi
139370
139589
  });
139371
139590
  };
139372
139591
  const handleSubmit = () => {
139373
- if (!itemName.trim() || !itemCode.trim()) {
139592
+ if (!formState.name?.trim() || !formState.code?.trim()) {
139374
139593
  toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
139375
139594
  return;
139376
139595
  }
@@ -139378,17 +139597,17 @@ function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoadi
139378
139597
  try {
139379
139598
  if (currentItem) {
139380
139599
  if (isControlled && onUpdate) {
139381
- await onUpdate({ id: currentItem.id, name: itemName, code: itemCode, subjectCode: selectedSubjectCode, description: itemDescription });
139600
+ await onUpdate({ ...currentItem, ...formState });
139382
139601
  } else {
139383
- MetadataService.updateLearningObjective(currentItem.id, itemName, itemCode, selectedSubjectCode, itemDescription);
139602
+ MetadataService.updateLearningObjective(currentItem.id, formState);
139384
139603
  refreshData();
139385
139604
  }
139386
139605
  toast2({ title: "Success", description: "Learning Objective updated." });
139387
139606
  } else {
139388
139607
  if (isControlled && onAdd) {
139389
- await onAdd({ name: itemName, code: itemCode, subjectCode: selectedSubjectCode, description: itemDescription });
139608
+ await onAdd(formState);
139390
139609
  } else {
139391
- MetadataService.addLearningObjective(itemName, itemCode, selectedSubjectCode, itemDescription);
139610
+ MetadataService.addLearningObjective(formState);
139392
139611
  refreshData();
139393
139612
  }
139394
139613
  toast2({ title: "Success", description: "Learning Objective added." });
@@ -139399,16 +139618,51 @@ function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoadi
139399
139618
  }
139400
139619
  });
139401
139620
  };
139402
- const getSubjectName = (subjectCode) => {
139403
- if (!subjectCode) return "N/A";
139404
- return subjects.find((s4) => s4.code === subjectCode)?.name || "N/A";
139621
+ const handleImport = async (records) => {
139622
+ if (!onBulkAdd) return;
139623
+ const parseStringToArray = (input) => {
139624
+ if (Array.isArray(input)) return input;
139625
+ if (typeof input === "string") return input.split(",").map((s4) => s4.trim()).filter(Boolean);
139626
+ return [];
139627
+ };
139628
+ const validatedRecords = records.reduce((acc, rec) => {
139629
+ if (typeof rec.code === "string" && rec.code.trim() && typeof rec.name === "string" && rec.name.trim()) {
139630
+ acc.push({
139631
+ code: rec.code,
139632
+ name: rec.name,
139633
+ description: rec.description,
139634
+ subject: rec.subject,
139635
+ subjectCode: rec.subjectCode,
139636
+ category: rec.category,
139637
+ categoryCode: rec.categoryCode,
139638
+ topic: rec.topic,
139639
+ topicCode: rec.topicCode,
139640
+ grade: rec.grade,
139641
+ gradeCode: rec.gradeCode,
139642
+ keywords: parseStringToArray(rec.keywords),
139643
+ stemElements: parseStringToArray(rec.stemElements),
139644
+ bloomLevelsGuideline: parseStringToArray(rec.bloomLevelsGuideline)
139645
+ });
139646
+ }
139647
+ return acc;
139648
+ }, []);
139649
+ if (validatedRecords.length !== records.length) {
139650
+ toast2({
139651
+ title: "Import Warning",
139652
+ description: `${records.length - validatedRecords.length} records had invalid or missing required fields ('code', 'name') and were ignored.`,
139653
+ variant: "destructive"
139654
+ });
139655
+ }
139656
+ if (validatedRecords.length > 0) {
139657
+ await onBulkAdd(validatedRecords);
139658
+ }
139405
139659
  };
139406
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Lightbulb, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Learning Objectives"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Learning Objective"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Learning Objectives found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, getSubjectName(item.subjectCode)), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Learning Objective" : "Add New Learning Objective")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value) })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemSubject" }, "Subject (Optional)"), /* @__PURE__ */ React169__default.createElement(Select2, { value: selectedSubjectCode || "", onValueChange: (value) => setSelectedSubjectCode(value === "_NONE_" ? void 0 : value) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, { id: "itemSubject" }, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select a subject" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, /* @__PURE__ */ React169__default.createElement(SelectItem2, { value: "_NONE_" }, "No Specific Subject"), subjects.map((subject) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: subject.id, value: subject.code }, subject.name))))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value) }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139660
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Lightbulb, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Learning Objectives"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__default.createElement(MetadataImportControls, { metadataName: "Learning Objectives", onImport: handleImport }), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Learning Objective")))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Learning Objectives found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Topic"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.subject || item.subjectCode), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.topic || item.topicCode), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Learning Objective" : "Add New Learning Objective")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "code" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "code", value: formState.code || "", onChange: (e3) => handleFormChange("code", e3.target.value.toUpperCase()), disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "name" }, "Name (Description)"), /* @__PURE__ */ React169__default.createElement(Input, { id: "name", value: formState.name || "", onChange: (e3) => handleFormChange("name", e3.target.value) }))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React169__default.createElement(Select2, { value: formState.subjectCode || "", onValueChange: (value) => handleFormChange("subjectCode", value) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, { placeholder: "Select a subject" })), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, subjects.map((subject) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: subject.id, value: subject.code }, subject.name))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "topicCode" }, "Topic Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "topicCode", value: formState.topicCode || "", onChange: (e3) => handleFormChange("topicCode", e3.target.value) }))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "categoryCode" }, "Category Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "categoryCode", value: formState.categoryCode || "", onChange: (e3) => handleFormChange("categoryCode", e3.target.value) })), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "gradeCode" }, "Grade Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "gradeCode", value: formState.gradeCode || "", onChange: (e3) => handleFormChange("gradeCode", e3.target.value) }))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "keywords" }, "Keywords (comma-separated)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "keywords", value: formState.keywords?.join(", ") || "", onChange: (e3) => handleFormChange("keywords", e3.target.value.split(",").map((s4) => s4.trim())) }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !formState.name?.trim() || !formState.code?.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139407
139661
  }
139408
139662
 
139409
139663
  // src/react-ui/components/metadata/ContextManager.tsx
139410
139664
  init_react_shim();
139411
- function ContextManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
139665
+ function ContextManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
139412
139666
  const [items, setItems] = useState([]);
139413
139667
  const [isLoading, setIsLoading] = useState(true);
139414
139668
  const [isDialogOpen, setIsDialogOpen] = useState(false);
@@ -139478,6 +139732,31 @@ function ContextManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate
139478
139732
  }
139479
139733
  });
139480
139734
  };
139735
+ const handleImport = async (records) => {
139736
+ if (!onBulkAdd) return;
139737
+ const validationResult = records.reduce((acc, rec) => {
139738
+ if (typeof rec.code === "string" && rec.code.trim() && typeof rec.name === "string" && rec.name.trim()) {
139739
+ acc.valid.push({
139740
+ code: rec.code,
139741
+ name: rec.name,
139742
+ description: typeof rec.description === "string" ? rec.description : void 0
139743
+ });
139744
+ } else {
139745
+ acc.invalidCount++;
139746
+ }
139747
+ return acc;
139748
+ }, { valid: [], invalidCount: 0 });
139749
+ if (validationResult.invalidCount > 0) {
139750
+ toast2({
139751
+ title: "Import Warning",
139752
+ description: `${validationResult.invalidCount} records had invalid or missing 'code' or 'name' fields and were ignored.`,
139753
+ variant: "destructive"
139754
+ });
139755
+ }
139756
+ if (validationResult.valid.length > 0) {
139757
+ await onBulkAdd(validationResult.valid);
139758
+ }
139759
+ };
139481
139760
  const handleSubmit = () => {
139482
139761
  if (!itemName.trim() || !itemCode.trim()) {
139483
139762
  toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
@@ -139508,13 +139787,13 @@ function ContextManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate
139508
139787
  }
139509
139788
  });
139510
139789
  };
139511
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(ScanText, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Contexts"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Context"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Contexts found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Context" : "Add New Context")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., HIST_INQ", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Historical Inquiry" })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Analyzing primary and secondary sources." }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139790
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(ScanText, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Contexts"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__default.createElement(MetadataImportControls, { metadataName: "Categories", onImport: handleImport }), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Context")))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Contexts found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Context" : "Add New Context")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., HIST_INQ", disabled: !!currentItem })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Historical Inquiry" })), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Analyzing primary and secondary sources." }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139512
139791
  }
139513
139792
 
139514
139793
  // src/react-ui/components/metadata/ApproachManager.tsx
139515
139794
  init_react_shim();
139516
139795
  var knowledgeDimensions = ["Factual", "Conceptual", "Procedural"];
139517
- var rawDifficultyLevels = ["E", "E~M", "M", "M~H", "H"];
139796
+ var standardDifficulties = ["Easy", "Medium", "Hard"];
139518
139797
  function ApproachManager({
139519
139798
  initialData,
139520
139799
  bloomLevels: bloomLevelsProp,
@@ -139522,7 +139801,8 @@ function ApproachManager({
139522
139801
  isLoading: isLoadingProp,
139523
139802
  onAdd,
139524
139803
  onUpdate,
139525
- onDelete
139804
+ onDelete,
139805
+ onBulkAdd
139526
139806
  }) {
139527
139807
  const [items, setItems] = useState([]);
139528
139808
  const [bloomLevels, setBloomLevels] = useState([]);
@@ -139563,7 +139843,7 @@ function ApproachManager({
139563
139843
  const resetForm = () => {
139564
139844
  setFormState({
139565
139845
  knowledgeDimension: "Factual",
139566
- rawDifficulty: "E",
139846
+ difficulty: ["Medium"],
139567
139847
  bloomLevelCode: bloomLevels.length > 0 ? bloomLevels[0].code : "",
139568
139848
  iSpringQuizType: questionTypes.length > 0 ? questionTypes[0].code : "multiple_choice"
139569
139849
  });
@@ -139575,18 +139855,7 @@ function ApproachManager({
139575
139855
  };
139576
139856
  const handleEditItem = (item) => {
139577
139857
  setCurrentItem(item);
139578
- setFormState({
139579
- code: item.code,
139580
- verbEn: item.verbEn,
139581
- verbVi: item.verbVi,
139582
- bloomLevelCode: item.bloomLevelCode,
139583
- knowledgeDimension: item.knowledgeDimension,
139584
- iSpringQuizType: item.iSpringQuizType,
139585
- rawDifficulty: item.rawDifficulty,
139586
- suggestContext: item.suggestContext,
139587
- exampleEn: item.exampleEn,
139588
- exampleVi: item.exampleVi
139589
- });
139858
+ setFormState(item);
139590
139859
  setIsDialogOpen(true);
139591
139860
  };
139592
139861
  const handleDeleteItem = (item) => {
@@ -139613,9 +139882,9 @@ function ApproachManager({
139613
139882
  });
139614
139883
  };
139615
139884
  const handleSubmit = () => {
139616
- const { code: code4, verbEn, verbVi, bloomLevelCode, iSpringQuizType, knowledgeDimension, rawDifficulty } = formState;
139617
- if (!code4?.trim() || !verbEn?.trim() || !verbVi?.trim() || !bloomLevelCode || !iSpringQuizType || !knowledgeDimension || !rawDifficulty) {
139618
- toast2({ title: "Validation Error", description: "All fields except examples and context are required.", variant: "destructive" });
139885
+ const { code: code4, name: name3, verbEn, verbVi, bloomLevelCode, iSpringQuizType, knowledgeDimension, difficulty } = formState;
139886
+ if (!code4?.trim() || !name3?.trim() || !verbEn?.trim() || !verbVi?.trim() || !bloomLevelCode || !iSpringQuizType || !knowledgeDimension || !difficulty || difficulty.length === 0) {
139887
+ toast2({ title: "Validation Error", description: "All fields except examples and context are required, and at least one difficulty must be selected.", variant: "destructive" });
139619
139888
  return;
139620
139889
  }
139621
139890
  startTransition(async () => {
@@ -139643,7 +139912,47 @@ function ApproachManager({
139643
139912
  }
139644
139913
  });
139645
139914
  };
139646
- return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Settings2, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Approaches"), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Approach"))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Approaches found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Approach ID"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Verb (VI)"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Cognitive Level"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "iSpring Type"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-medium" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.verbVi), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.bloomLevelCode), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.iSpringQuizType), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Approach" : "Add New Approach")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "code" }, "Approach Code"), /* @__PURE__ */ React169__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__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "verbEn" }, "Verb (English)"), /* @__PURE__ */ React169__default.createElement(Input, { id: "verbEn", value: formState.verbEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, verbEn: e3.target.value })), placeholder: "e.g., Identify" }))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "verbVi" }, "Verb (Vietnamese)"), /* @__PURE__ */ React169__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__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "bloomLevelCode" }, "Cognitive Level"), /* @__PURE__ */ React169__default.createElement(Select2, { value: formState.bloomLevelCode, onValueChange: (v) => setFormState((s4) => ({ ...s4, bloomLevelCode: v })) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, bloomLevels.map((level) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: level.code, value: level.code }, level.name)))))), /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "knowledgeDimension" }, "Knowledge Dimension"), /* @__PURE__ */ React169__default.createElement(Select2, { value: formState.knowledgeDimension, onValueChange: (v) => setFormState((s4) => ({ ...s4, knowledgeDimension: v })) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, knowledgeDimensions.map((kd) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: kd, value: kd }, kd))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "iSpringQuizType" }, "iSpring Quiz Type"), /* @__PURE__ */ React169__default.createElement(Select2, { value: formState.iSpringQuizType, onValueChange: (v) => setFormState((s4) => ({ ...s4, iSpringQuizType: v })) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, questionTypes.map((qt) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: qt.code, value: qt.code }, qt.name))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "rawDifficulty" }, "Raw Difficulty"), /* @__PURE__ */ React169__default.createElement(Select2, { value: formState.rawDifficulty, onValueChange: (v) => setFormState((s4) => ({ ...s4, rawDifficulty: v })) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, rawDifficultyLevels.map((rd) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: rd, value: rd }, rd)))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "suggestContext" }, "Suggest Context (comma-separated codes)"), /* @__PURE__ */ React169__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__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "exampleEn" }, "Example (English)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "exampleEn", value: formState.exampleEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, exampleEn: e3.target.value })), placeholder: "English example prompt..." })), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "exampleVi" }, "Example (Vietnamese)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "exampleVi", value: formState.exampleVi || "", onChange: (e3) => setFormState((s4) => ({ ...s4, exampleVi: e3.target.value })), placeholder: "Vietnamese example prompt..." }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.code, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139915
+ const handleImport = async (records) => {
139916
+ if (!onBulkAdd) return;
139917
+ const parseStringToArray = (input) => {
139918
+ if (Array.isArray(input)) return input;
139919
+ if (typeof input === "string") return input.split(",").map((s4) => s4.trim()).filter(Boolean);
139920
+ return [];
139921
+ };
139922
+ const validatedRecords = records.reduce((acc, rec) => {
139923
+ if (rec.code && rec.name && rec.verbEn && rec.verbVi && rec.knowledgeDimension && rec.iSpringQuizType && rec.difficulty && rec.bloomLevelCode) {
139924
+ acc.push({
139925
+ code: rec.code,
139926
+ name: rec.name,
139927
+ verbEn: rec.verbEn,
139928
+ verbVi: rec.verbVi,
139929
+ knowledgeDimension: rec.knowledgeDimension,
139930
+ iSpringQuizType: rec.iSpringQuizType,
139931
+ difficulty: parseStringToArray(rec.difficulty),
139932
+ bloomLevelCode: rec.bloomLevelCode,
139933
+ suggestContext: rec.suggestContext,
139934
+ exampleEn: rec.exampleEn,
139935
+ exampleVi: rec.exampleVi
139936
+ });
139937
+ }
139938
+ return acc;
139939
+ }, []);
139940
+ if (validatedRecords.length !== records.length) {
139941
+ toast2({
139942
+ title: "Import Warning",
139943
+ description: `${records.length - validatedRecords.length} records had missing required fields and were ignored.`,
139944
+ variant: "destructive"
139945
+ });
139946
+ }
139947
+ if (validatedRecords.length > 0) {
139948
+ await onBulkAdd(validatedRecords);
139949
+ }
139950
+ };
139951
+ return /* @__PURE__ */ React169__default.createElement(Card, null, /* @__PURE__ */ React169__default.createElement(CardHeader, null, /* @__PURE__ */ React169__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__default.createElement(Settings2, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Approaches"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__default.createElement(MetadataImportControls, { metadataName: "Approaches", onImport: handleImport }), /* @__PURE__ */ React169__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Approach")))), /* @__PURE__ */ React169__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Approaches found.") : /* @__PURE__ */ React169__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__default.createElement(Table3, null, /* @__PURE__ */ React169__default.createElement(TableHeader, null, /* @__PURE__ */ React169__default.createElement(TableRow, null, /* @__PURE__ */ React169__default.createElement(TableHead, null, "Approach ID"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Verb (VI)"), /* @__PURE__ */ React169__default.createElement(TableHead, null, "Cognitive Level"), /* @__PURE__ */ React169__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__default.createElement(TableCell, { className: "font-Medium" }, item.code), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.name), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.verbVi), /* @__PURE__ */ React169__default.createElement(TableCell, null, item.bloomLevelCode), /* @__PURE__ */ React169__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__default.createElement(DialogContent2, { className: "sm:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, currentItem ? "Edit Approach" : "Add New Approach")), /* @__PURE__ */ React169__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "code" }, "Approach Code"), /* @__PURE__ */ React169__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__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "name" }, "Name / Description"), /* @__PURE__ */ React169__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__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "verbEn" }, "Verb (English)"), /* @__PURE__ */ React169__default.createElement(Input, { id: "verbEn", value: formState.verbEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, verbEn: e3.target.value })), placeholder: "e.g., Identify" })), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "verbVi" }, "Verb (Vietnamese)"), /* @__PURE__ */ React169__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__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4" }, /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "bloomLevelCode" }, "Cognitive Level"), /* @__PURE__ */ React169__default.createElement(Select2, { value: formState.bloomLevelCode, onValueChange: (v) => setFormState((s4) => ({ ...s4, bloomLevelCode: v })) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, bloomLevels.map((level) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: level.code, value: level.code }, level.name))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "knowledgeDimension" }, "Knowledge Dimension"), /* @__PURE__ */ React169__default.createElement(Select2, { value: formState.knowledgeDimension, onValueChange: (v) => setFormState((s4) => ({ ...s4, knowledgeDimension: v })) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, knowledgeDimensions.map((kd) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: kd, value: kd }, kd))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "iSpringQuizType" }, "iSpring Quiz Type"), /* @__PURE__ */ React169__default.createElement(Select2, { value: formState.iSpringQuizType, onValueChange: (v) => setFormState((s4) => ({ ...s4, iSpringQuizType: v })) }, /* @__PURE__ */ React169__default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__default.createElement(SelectContent2, null, questionTypes.map((qt) => /* @__PURE__ */ React169__default.createElement(SelectItem2, { key: qt.code, value: qt.code }, qt.name)))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, null, "Difficulty"), /* @__PURE__ */ React169__default.createElement("div", { className: "flex items-center space-x-4 pt-2" }, standardDifficulties.map((diff2) => /* @__PURE__ */ React169__default.createElement("div", { key: diff2, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__default.createElement(Checkbox2, { id: `diff-${diff2}`, checked: (formState.difficulty || []).includes(diff2), onCheckedChange: (checked) => {
139952
+ const current = formState.difficulty || [];
139953
+ const newDiff = checked ? [...current, diff2] : current.filter((d) => d !== diff2);
139954
+ setFormState((s4) => ({ ...s4, difficulty: newDiff }));
139955
+ } }), /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: `diff-${diff2}` }, diff2))))), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "suggestContext" }, "Suggest Context (comma-separated codes)"), /* @__PURE__ */ React169__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__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "exampleEn" }, "Example (English)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "exampleEn", value: formState.exampleEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, exampleEn: e3.target.value })), placeholder: "English example prompt..." })), /* @__PURE__ */ React169__default.createElement("div", null, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "exampleVi" }, "Example (Vietnamese)"), /* @__PURE__ */ React169__default.createElement(Textarea, { id: "exampleVi", value: formState.exampleVi || "", onChange: (e3) => setFormState((s4) => ({ ...s4, exampleVi: e3.target.value })), placeholder: "Vietnamese example prompt..." }))), /* @__PURE__ */ React169__default.createElement(DialogFooter, null, /* @__PURE__ */ React169__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.code, '".')), /* @__PURE__ */ React169__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139647
139956
  }
139648
139957
 
139649
139958
  // src/react-ui/components/metadata/MetadataTabs.tsx
@@ -140319,7 +140628,7 @@ var ToastAction2 = React169.forwardRef(({ className, ...props }, ref) => /* @__P
140319
140628
  {
140320
140629
  ref,
140321
140630
  className: cn(
140322
- "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
140631
+ "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-Medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
140323
140632
  className
140324
140633
  ),
140325
140634
  ...props
@@ -141914,4 +142223,4 @@ react-tooltip/dist/react-tooltip.min.mjs:
141914
142223
  *)
141915
142224
  */
141916
142225
 
141917
- export { AIFullQuizGeneratorModal, AIQuestionGeneratorModal, APIKeyManagerModal, Accordion2 as Accordion, AccordionContent2 as AccordionContent, AccordionItem2 as AccordionItem, AccordionTrigger2 as AccordionTrigger, Achievements, ActivityCalendar, Alert, AlertDescription, AlertTitle, ApiKeySettings, ApproachManager, Badge2 as Badge, BloomLevelManager, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryManager, Cheatsheet, Checkbox2 as Checkbox, ContextManager, Dialog2 as Dialog, DialogContent2 as DialogContent, DialogDescription2 as DialogDescription, DialogFooter, DialogHeader, DialogTitle2 as DialogTitle, EditQuestionModal, FreestyleQuizzesCard, GeneratedQuizzesCard, GradeLevelManager, ImportQuestionsModal, Input, Label2 as Label, LanguageProvider, LearningObjectiveManager, ManageTopics, MetadataTabs, PerformanceCharts, PersonalPracticeDashboard, PracticeHistoryTable, PracticeModeController, Progress2 as Progress, QuestionFilters, QuestionFormDialog, QuestionList, QuestionPreviewModal, QuestionRenderer, QuestionTypeManager, QuizAuthoringTool, QuizDataManagement, QuizPlayer, QuizResult, QuizReview, QuizSettingsForm, RadioGroup2 as RadioGroup, RadioGroupItem2 as RadioGroupItem, SCORMExportModal, ScrollArea2 as ScrollArea, Select2 as Select, SelectContent2 as SelectContent, SelectItem2 as SelectItem, SelectTrigger2 as SelectTrigger, SelectValue2 as SelectValue, SelectedQuestionsPanel, SettingsModal, Skeleton, SubjectManager, SuggestionDialog, Tabs2 as Tabs, TabsContent2 as TabsContent, TabsList2 as TabsList, TabsTrigger2 as TabsTrigger, Toaster, Tooltip2 as Tooltip, TooltipContent2 as TooltipContent, TooltipProvider2 as TooltipProvider, TooltipTrigger2 as TooltipTrigger, TopicManager, UploadResourceModal, toast, useToast };
142226
+ export { AIFullQuizGeneratorModal, AIQuestionGeneratorModal, APIKeyManagerModal, Accordion2 as Accordion, AccordionContent2 as AccordionContent, AccordionItem2 as AccordionItem, AccordionTrigger2 as AccordionTrigger, Achievements, ActivityCalendar, Alert, AlertDescription, AlertTitle, ApiKeySettings, ApproachManager, Badge2 as Badge, BloomLevelManager, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryManager, Cheatsheet, Checkbox2 as Checkbox, ContextManager, Dialog2 as Dialog, DialogContent2 as DialogContent, DialogDescription2 as DialogDescription, DialogFooter, DialogHeader, DialogTitle2 as DialogTitle, EditQuestionModal, FreestyleQuizzesCard, GeneratedQuizzesCard, GradeLevelManager, ImportQuestionsModal, Input, Label2 as Label, LanguageProvider, LearningObjectiveManager, ManageTopics, MetadataImportControls, MetadataTabs, PerformanceCharts, PersonalPracticeDashboard, PracticeHistoryTable, PracticeModeController, Progress2 as Progress, QuestionFilters, QuestionFormDialog, QuestionList, QuestionPreviewModal, QuestionRenderer, QuestionTypeManager, QuizAuthoringTool, QuizDataManagement, QuizPlayer, QuizResult, QuizReview, QuizSettingsForm, RadioGroup2 as RadioGroup, RadioGroupItem2 as RadioGroupItem, SCORMExportModal, ScrollArea2 as ScrollArea, Select2 as Select, SelectContent2 as SelectContent, SelectItem2 as SelectItem, SelectTrigger2 as SelectTrigger, SelectValue2 as SelectValue, SelectedQuestionsPanel, SettingsModal, Skeleton, SubjectManager, SuggestionDialog, Tabs2 as Tabs, TabsContent2 as TabsContent, TabsList2 as TabsList, TabsTrigger2 as TabsTrigger, Toaster, Tooltip2 as Tooltip, TooltipContent2 as TooltipContent, TooltipProvider2 as TooltipProvider, TooltipTrigger2 as TooltipTrigger, TopicManager, UploadResourceModal, toast, useToast };