@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.cjs CHANGED
@@ -9085,7 +9085,7 @@ var translation_default = {
9085
9085
  subject: "Subject",
9086
9086
  category: "Category",
9087
9087
  topic: "Topic",
9088
- loId: "LO ID"
9088
+ code: "LO ID"
9089
9089
  },
9090
9090
  confirmModal: {
9091
9091
  title: "Confirm Data Import",
@@ -9500,7 +9500,7 @@ var translation_default2 = {
9500
9500
  subject: "M\xF4n h\u1ECDc",
9501
9501
  category: "Danh m\u1EE5c",
9502
9502
  topic: "Ch\u1EE7 \u0111\u1EC1",
9503
- loId: "M\xE3 MTH"
9503
+ code: "M\xE3 MTH"
9504
9504
  },
9505
9505
  confirmModal: {
9506
9506
  title: "X\xE1c nh\u1EADn Nh\u1EADp D\u1EEF li\u1EC7u",
@@ -37572,7 +37572,7 @@ var cva = (base3, config3) => (props) => {
37572
37572
 
37573
37573
  // src/react-ui/components/elements/label.tsx
37574
37574
  var labelVariants = cva(
37575
- "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
37575
+ "text-sm font-Medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
37576
37576
  );
37577
37577
  var Label2 = React169__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React169__namespace.createElement(
37578
37578
  Root3,
@@ -62810,7 +62810,7 @@ var MarkdownRenderer = ({
62810
62810
  },
62811
62811
  h1: ({ node: node2, ...props }) => /* @__PURE__ */ React169__namespace.default.createElement("h1", { ...props, className: "text-3xl font-bold mb-6 mt-8 first:mt-0" }),
62812
62812
  h2: ({ node: node2, ...props }) => /* @__PURE__ */ React169__namespace.default.createElement("h2", { ...props, className: "text-2xl font-semibold mb-4 mt-6" }),
62813
- h3: ({ node: node2, ...props }) => /* @__PURE__ */ React169__namespace.default.createElement("h3", { ...props, className: "text-xl font-medium mb-3 mt-5" }),
62813
+ h3: ({ node: node2, ...props }) => /* @__PURE__ */ React169__namespace.default.createElement("h3", { ...props, className: "text-xl font-Medium mb-3 mt-5" }),
62814
62814
  ul: ({ node: node2, ...props }) => /* @__PURE__ */ React169__namespace.default.createElement("ul", { ...props, className: "my-4 space-y-2 list-disc list-inside" }),
62815
62815
  ol: ({ node: node2, ...props }) => /* @__PURE__ */ React169__namespace.default.createElement("ol", { ...props, className: "my-4 space-y-2 list-decimal list-inside" }),
62816
62816
  p: ({ node: node2, ...props }) => /* @__PURE__ */ React169__namespace.default.createElement("p", { ...props, className: "mb-4 leading-7" }),
@@ -63284,7 +63284,7 @@ var Input = React169__namespace.forwardRef(
63284
63284
  {
63285
63285
  type,
63286
63286
  className: cn(
63287
- "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",
63287
+ "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",
63288
63288
  className
63289
63289
  ),
63290
63290
  ref,
@@ -63459,7 +63459,7 @@ init_react_shim();
63459
63459
  // src/react-ui/components/elements/button.tsx
63460
63460
  init_react_shim();
63461
63461
  var buttonVariants = cva(
63462
- "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",
63462
+ "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",
63463
63463
  {
63464
63464
  variants: {
63465
63465
  variant: {
@@ -68412,7 +68412,7 @@ var MatchingQuestionUI = ({
68412
68412
  if (showCorrectAnswer && selectedOptionId) {
68413
68413
  borderColor = isSelectionCorrect ? "border-green-500" : "border-destructive";
68414
68414
  }
68415
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { key: promptItem.id, className: `p-3 border rounded-md ${borderColor} transition-colors bg-background` }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `select-prompt-${promptItem.id}`, className: "font-medium text-base block mb-2 whitespace-normal" }, /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: promptItem.content })), /* @__PURE__ */ React169__namespace.default.createElement(
68415
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { key: promptItem.id, className: `p-3 border rounded-md ${borderColor} transition-colors bg-background` }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `select-prompt-${promptItem.id}`, className: "font-Medium text-base block mb-2 whitespace-normal" }, /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: promptItem.content })), /* @__PURE__ */ React169__namespace.default.createElement(
68416
68416
  Select2,
68417
68417
  {
68418
68418
  value: selectedOptionId,
@@ -68467,7 +68467,7 @@ var DragAndDropQuestionUI = ({
68467
68467
  } else {
68468
68468
  itemStyle += " border-muted";
68469
68469
  }
68470
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { key: item.id, className: itemStyle }, /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(MarkdownRenderer, { content: item.content })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-2 w-full sm:w-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(
68470
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { key: item.id, className: itemStyle }, /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(MarkdownRenderer, { content: item.content })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-2 w-full sm:w-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(
68471
68471
  Select2,
68472
68472
  {
68473
68473
  value: selectedDropZoneId,
@@ -95483,7 +95483,7 @@ var TabsTrigger2 = React169__namespace.forwardRef(({ className, ...props }, ref)
95483
95483
  {
95484
95484
  ref,
95485
95485
  className: cn(
95486
- "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",
95486
+ "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",
95487
95487
  className
95488
95488
  ),
95489
95489
  ...props
@@ -96183,7 +96183,7 @@ var AccordionTrigger2 = React169__namespace.forwardRef(({ className, children, .
96183
96183
  {
96184
96184
  ref,
96185
96185
  className: cn(
96186
- "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
96186
+ "flex flex-1 items-center justify-between py-4 font-Medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
96187
96187
  className
96188
96188
  ),
96189
96189
  ...props
@@ -96970,7 +96970,7 @@ var QuizResult = ({
96970
96970
  }
96971
96971
  return String(answer);
96972
96972
  };
96973
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "w-full max-w-3xl mx-auto shadow-xl" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-3xl font-headline text-center" }, t4("practiceFlow.results.title", { quizTitle })), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, { className: "text-center text-lg" }, t4("practiceFlow.results.description"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "bg-secondary/50" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-xl flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(BarChart2, { className: "mr-2 h-5 w-5 text-primary" }), t4("practiceFlow.results.overallScore"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "grid grid-cols-1 md:grid-cols-3 gap-4 text-center" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-3xl font-bold text-primary" }, result.score, " / ", result.maxScore), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("practiceFlow.results.points"))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-3xl font-bold text-primary" }, result.percentage.toFixed(2), "%"), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("practiceFlow.results.percentage"))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, result.passed !== void 0 && (result.passed ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col items-center text-green-600" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleCheckBig, { className: "h-10 w-10" }), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xl font-semibold mt-1" }, t4("practiceFlow.results.passed"))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col items-center text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleX, { className: "h-10 w-10" }), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xl font-semibold mt-1" }, t4("practiceFlow.results.failed"))))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 text-sm" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement(Clock, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ React169__namespace.default.createElement("span", null, t4("practiceFlow.results.timeSpent")), /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-semibold" }, result.totalTimeSpentSeconds?.toFixed(0) ?? "N/A", " ", t4("practiceFlow.results.timeUnit"))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement(Percent, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ React169__namespace.default.createElement("span", null, t4("practiceFlow.results.avgTimePerQuestion")), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-lg" }, "SCORM Sync Status")), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: `flex items-center ${result.scormStatus === "error" ? "text-destructive" : "text-muted-foreground"}` }, result.scormStatus === "error" && /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "mr-2 h-4 w-4" }), "Status: ", /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-semibold ml-1" }, result.scormStatus)), result.scormError && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-destructive mt-1" }, "Details: ", result.scormError))), result.webhookStatus && result.webhookStatus !== "idle" && /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-lg" }, "Webhook Sync Status")), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: `flex items-center ${result.webhookStatus === "error" ? "text-destructive" : "text-muted-foreground"}` }, result.webhookStatus === "error" && /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "mr-2 h-4 w-4" }), "Status: ", /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-semibold ml-1" }, result.webhookStatus)), result.webhookError && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-destructive mt-1" }, "Details: ", result.webhookError))), /* @__PURE__ */ React169__namespace.default.createElement(Accordion2, { type: "single", collapsible: true, className: "w-full" }, /* @__PURE__ */ React169__namespace.default.createElement(AccordionItem2, { value: "question-breakdown" }, /* @__PURE__ */ React169__namespace.default.createElement(AccordionTrigger2, { className: "text-lg font-semibold" }, t4("practiceFlow.results.questionBreakdown")), /* @__PURE__ */ React169__namespace.default.createElement(AccordionContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-[300px] pr-4" }, /* @__PURE__ */ React169__namespace.default.createElement("ul", { className: "space-y-4" }, result.questionResults.map((qResult, index3) => /* @__PURE__ */ React169__namespace.default.createElement("li", { key: qResult.questionId, className: "p-4 border rounded-md bg-background" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold" }, t4("common.questions", { count: index3 + 1 })), qResult.isCorrect ? /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-green-600 font-medium flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleCheckBig, { className: "mr-1 h-4 w-4" }), " ", t4("practiceFlow.results.passed")) : /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-destructive font-medium flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleX, { className: "mr-1 h-4 w-4" }), " ", t4("practiceFlow.results.failed"))), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-medium" }, t4("practiceFlow.results.yourAnswer")), " ", getAnswerDisplay(qResult.userAnswer)), !qResult.isCorrect && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-medium" }, t4("practiceFlow.results.correctAnswer")), " ", getAnswerDisplay(qResult.correctAnswer)), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-muted-foreground" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-medium" }, t4("practiceFlow.results.pointsEarned")), " ", qResult.pointsEarned), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-muted-foreground" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-medium" }, t4("practiceFlow.results.timeSpent")), " ", qResult.timeSpentSeconds?.toFixed(0) ?? "N/A", t4("practiceFlow.results.timeUnit")))))))))), /* @__PURE__ */ React169__namespace.default.createElement(CardFooter, { className: "flex flex-col sm:flex-row justify-between gap-2" }, onExitQuiz && /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "outline", onClick: onExitQuiz, className: "w-full sm:w-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(LogOut, { className: "mr-2 h-4 w-4" }), t4("common.exit")), showReviewButton && onGenerateReview && /* @__PURE__ */ React169__namespace.default.createElement(
96973
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "w-full max-w-3xl mx-auto shadow-xl" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-3xl font-headline text-center" }, t4("practiceFlow.results.title", { quizTitle })), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, { className: "text-center text-lg" }, t4("practiceFlow.results.description"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "bg-secondary/50" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-xl flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(BarChart2, { className: "mr-2 h-5 w-5 text-primary" }), t4("practiceFlow.results.overallScore"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "grid grid-cols-1 md:grid-cols-3 gap-4 text-center" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-3xl font-bold text-primary" }, result.score, " / ", result.maxScore), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("practiceFlow.results.points"))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-3xl font-bold text-primary" }, result.percentage.toFixed(2), "%"), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("practiceFlow.results.percentage"))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, result.passed !== void 0 && (result.passed ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col items-center text-green-600" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleCheckBig, { className: "h-10 w-10" }), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xl font-semibold mt-1" }, t4("practiceFlow.results.passed"))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col items-center text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleX, { className: "h-10 w-10" }), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xl font-semibold mt-1" }, t4("practiceFlow.results.failed"))))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 text-sm" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement(Clock, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ React169__namespace.default.createElement("span", null, t4("practiceFlow.results.timeSpent")), /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-semibold" }, result.totalTimeSpentSeconds?.toFixed(0) ?? "N/A", " ", t4("practiceFlow.results.timeUnit"))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-2 p-3 bg-muted rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement(Percent, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ React169__namespace.default.createElement("span", null, t4("practiceFlow.results.avgTimePerQuestion")), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-lg" }, "SCORM Sync Status")), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: `flex items-center ${result.scormStatus === "error" ? "text-destructive" : "text-muted-foreground"}` }, result.scormStatus === "error" && /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "mr-2 h-4 w-4" }), "Status: ", /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-semibold ml-1" }, result.scormStatus)), result.scormError && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-destructive mt-1" }, "Details: ", result.scormError))), result.webhookStatus && result.webhookStatus !== "idle" && /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-lg" }, "Webhook Sync Status")), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: `flex items-center ${result.webhookStatus === "error" ? "text-destructive" : "text-muted-foreground"}` }, result.webhookStatus === "error" && /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "mr-2 h-4 w-4" }), "Status: ", /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-semibold ml-1" }, result.webhookStatus)), result.webhookError && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-destructive mt-1" }, "Details: ", result.webhookError))), /* @__PURE__ */ React169__namespace.default.createElement(Accordion2, { type: "single", collapsible: true, className: "w-full" }, /* @__PURE__ */ React169__namespace.default.createElement(AccordionItem2, { value: "question-breakdown" }, /* @__PURE__ */ React169__namespace.default.createElement(AccordionTrigger2, { className: "text-lg font-semibold" }, t4("practiceFlow.results.questionBreakdown")), /* @__PURE__ */ React169__namespace.default.createElement(AccordionContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-[300px] pr-4" }, /* @__PURE__ */ React169__namespace.default.createElement("ul", { className: "space-y-4" }, result.questionResults.map((qResult, index3) => /* @__PURE__ */ React169__namespace.default.createElement("li", { key: qResult.questionId, className: "p-4 border rounded-md bg-background" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold" }, t4("common.questions", { count: index3 + 1 })), qResult.isCorrect ? /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-green-600 font-Medium flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleCheckBig, { className: "mr-1 h-4 w-4" }), " ", t4("practiceFlow.results.passed")) : /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-destructive font-Medium flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleX, { className: "mr-1 h-4 w-4" }), " ", t4("practiceFlow.results.failed"))), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-Medium" }, t4("practiceFlow.results.yourAnswer")), " ", getAnswerDisplay(qResult.userAnswer)), !qResult.isCorrect && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-Medium" }, t4("practiceFlow.results.correctAnswer")), " ", getAnswerDisplay(qResult.correctAnswer)), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-muted-foreground" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-Medium" }, t4("practiceFlow.results.pointsEarned")), " ", qResult.pointsEarned), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-muted-foreground" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-Medium" }, t4("practiceFlow.results.timeSpent")), " ", qResult.timeSpentSeconds?.toFixed(0) ?? "N/A", t4("practiceFlow.results.timeUnit")))))))))), /* @__PURE__ */ React169__namespace.default.createElement(CardFooter, { className: "flex flex-col sm:flex-row justify-between gap-2" }, onExitQuiz && /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "outline", onClick: onExitQuiz, className: "w-full sm:w-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(LogOut, { className: "mr-2 h-4 w-4" }), t4("common.exit")), showReviewButton && onGenerateReview && /* @__PURE__ */ React169__namespace.default.createElement(
96974
96974
  Button,
96975
96975
  {
96976
96976
  onClick: onGenerateReview,
@@ -97285,7 +97285,7 @@ var QuizDataManagement = ({ onQuizLoad, currentQuiz }) => {
97285
97285
  });
97286
97286
  }
97287
97287
  };
97288
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "w-full shadow-lg" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, null, "Quiz Data Management"), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, null, "Import a quiz from a JSON file or export the current quiz configuration.")), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col gap-2 mb-4" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "quiz-file-input", className: "text-sm font-medium" }, "Import Quiz (JSON)"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "relative" }, /* @__PURE__ */ React169__namespace.default.createElement(
97288
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "w-full shadow-lg" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, null, "Quiz Data Management"), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, null, "Import a quiz from a JSON file or export the current quiz configuration.")), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col gap-2 mb-4" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "quiz-file-input", className: "text-sm font-Medium" }, "Import Quiz (JSON)"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "relative" }, /* @__PURE__ */ React169__namespace.default.createElement(
97289
97289
  Input,
97290
97290
  {
97291
97291
  id: "quiz-file-input",
@@ -97293,7 +97293,7 @@ var QuizDataManagement = ({ onQuizLoad, currentQuiz }) => {
97293
97293
  accept: ".json",
97294
97294
  ref: fileInputRef,
97295
97295
  onChange: handleFileChange,
97296
- 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"
97296
+ 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"
97297
97297
  }
97298
97298
  ))), error && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-destructive flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleAlert, { className: "mr-1 h-4 w-4" }), " ", error)), /* @__PURE__ */ React169__namespace.default.createElement(CardFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleExportQuiz, disabled: !currentQuiz, variant: "outline" }, /* @__PURE__ */ React169__namespace.default.createElement(Download, { className: "mr-2 h-4 w-4" }), " Export Current Quiz")));
97299
97299
  };
@@ -97728,6 +97728,7 @@ var Close = DialogClose;
97728
97728
 
97729
97729
  // src/react-ui/components/elements/dialog.tsx
97730
97730
  var Dialog2 = Root10;
97731
+ var DialogTrigger2 = Trigger4;
97731
97732
  var DialogPortal2 = Portal3;
97732
97733
  var DialogClose2 = Close;
97733
97734
  var DialogOverlay2 = React169__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React169__namespace.createElement(
@@ -97926,7 +97927,7 @@ var CommandGroup = React169__namespace.forwardRef(({ className, ...props }, ref)
97926
97927
  {
97927
97928
  ref,
97928
97929
  className: cn(
97929
- "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",
97930
+ "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",
97930
97931
  className
97931
97932
  ),
97932
97933
  ...props
@@ -98327,7 +98328,7 @@ var TrueFalseQuestionForm = ({ question: question2, onFormChange }) => {
98327
98328
  const handleCorrectAnswerChange = (value) => {
98328
98329
  onFormChange({ correctAnswer: value === "true" });
98329
98330
  };
98330
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "True/False Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Correct Answer"), /* @__PURE__ */ React169__namespace.default.createElement(
98331
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "True/False Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Correct Answer"), /* @__PURE__ */ React169__namespace.default.createElement(
98331
98332
  RadioGroup2,
98332
98333
  {
98333
98334
  value: question2.correctAnswer ? "true" : "false",
@@ -98364,7 +98365,7 @@ var MultipleChoiceQuestionForm = ({ question: question2, onFormChange }) => {
98364
98365
  const handleCorrectAnswerChange = (optionId) => {
98365
98366
  onFormChange({ correctAnswerId: optionId });
98366
98367
  };
98367
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Multiple Choice Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Options"), question2.options.length === 0 && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No options added yet."), /* @__PURE__ */ React169__namespace.default.createElement(
98368
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Multiple Choice Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Options"), question2.options.length === 0 && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No options added yet."), /* @__PURE__ */ React169__namespace.default.createElement(
98368
98369
  RadioGroup2,
98369
98370
  {
98370
98371
  value: question2.correctAnswerId,
@@ -98426,7 +98427,7 @@ var MultipleResponseQuestionForm = ({ question: question2, onFormChange }) => {
98426
98427
  const newCorrectAnswerIds = question2.correctAnswerIds.filter((id3) => id3 !== optionIdToDelete);
98427
98428
  onFormChange({ options: newOptions, correctAnswerIds: newCorrectAnswerIds });
98428
98429
  };
98429
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Multiple Response Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Options (Select all correct answers)"), question2.options.length === 0 && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No options added yet."), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3 mt-2" }, question2.options.map((option, index3) => (
98430
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Multiple Response Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Options (Select all correct answers)"), question2.options.length === 0 && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No options added yet."), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3 mt-2" }, question2.options.map((option, index3) => (
98430
98431
  // *** CHANGED: Adjusted layout for SimpleMarkdownEditor ***
98431
98432
  /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(
98432
98433
  Checkbox2,
@@ -98648,7 +98649,7 @@ var ShortAnswerQuestionForm = ({ question: question2, onFormChange }) => {
98648
98649
  const toggleCaseSensitive = (checked) => {
98649
98650
  onFormChange({ isCaseSensitive: checked });
98650
98651
  };
98651
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Short Answer Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Accepted Answers"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No accepted answers defined yet."), question2.acceptedAnswers.map((answer, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: index3, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
98652
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Short Answer Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Accepted Answers"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("p", { className: "text-sm text-muted-foreground mt-1" }, "No accepted answers defined yet."), question2.acceptedAnswers.map((answer, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: index3, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
98652
98653
  Input,
98653
98654
  {
98654
98655
  type: "text",
@@ -98698,7 +98699,7 @@ var NumericQuestionForm = ({ question: question2, onFormChange }) => {
98698
98699
  onFormChange({ tolerance: numValue });
98699
98700
  }
98700
98701
  };
98701
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Numeric Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `num-answer-${question2.id}` }, "Correct Numerical Answer"), /* @__PURE__ */ React169__namespace.default.createElement(
98702
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Numeric Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `num-answer-${question2.id}` }, "Correct Numerical Answer"), /* @__PURE__ */ React169__namespace.default.createElement(
98702
98703
  Input,
98703
98704
  {
98704
98705
  id: `num-answer-${question2.id}`,
@@ -98811,7 +98812,7 @@ var FillInTheBlanksQuestionForm = ({ question: question2, onFormChange }) => {
98811
98812
  const toggleCaseSensitive = (checked) => {
98812
98813
  onFormChange({ isCaseSensitive: checked });
98813
98814
  };
98814
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Fill In The Blanks Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Question Segments"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { key: `segment-${index3}`, className: "flex items-start space-x-2 mt-2 p-2 border rounded-md bg-background" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex-grow space-y-1" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-xs font-medium capitalize text-muted-foreground" }, segment.type, " Segment ", index3 + 1), segment.type === "text" ? (
98815
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Fill In The Blanks Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Question Segments"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { key: `segment-${index3}`, className: "flex items-start space-x-2 mt-2 p-2 border rounded-md bg-background" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex-grow space-y-1" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-xs font-Medium capitalize text-muted-foreground" }, segment.type, " Segment ", index3 + 1), segment.type === "text" ? (
98815
98816
  // *** CHANGED: Replaced Textarea with SimpleMarkdownEditor ***
98816
98817
  /* @__PURE__ */ React169__namespace.default.createElement(
98817
98818
  SimpleMarkdownEditor,
@@ -98884,7 +98885,7 @@ var SequenceQuestionForm = ({ question: question2, onFormChange }) => {
98884
98885
  }
98885
98886
  return htmlString.replace(/<[^>]*>?/gm, "");
98886
98887
  };
98887
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Sequence Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Sequence Items"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "space-y-3 mt-2" }, question2.items.map((item, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: item.id, className: "flex items-start space-x-2 p-2 border rounded-md bg-background" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex-grow" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "text-xs font-medium text-muted-foreground" }, "Item ", index3 + 1), /* @__PURE__ */ React169__namespace.default.createElement(
98888
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Sequence Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Sequence Items"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "space-y-3 mt-2" }, question2.items.map((item, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: item.id, className: "flex items-start space-x-2 p-2 border rounded-md bg-background" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex-grow" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "text-xs font-Medium text-muted-foreground" }, "Item ", index3 + 1), /* @__PURE__ */ React169__namespace.default.createElement(
98888
98889
  SimpleMarkdownEditor,
98889
98890
  {
98890
98891
  value: item.content,
@@ -98987,7 +98988,7 @@ var MatchingQuestionForm = ({ question: question2, onFormChange }) => {
98987
98988
  }
98988
98989
  return htmlString.replace(/<[^>]*>?/gm, "");
98989
98990
  };
98990
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Matching Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Prompts (Items to be matched)"), question2.prompts.map((promptItem, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: promptItem.id, className: "space-y-1" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "text-xs text-muted-foreground" }, "Prompt ", index3 + 1), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeletePrompt(index3), className: "h-7 w-7 text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__namespace.default.createElement(
98991
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Matching Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Prompts (Items to be matched)"), question2.prompts.map((promptItem, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: promptItem.id, className: "space-y-1" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "text-xs text-muted-foreground" }, "Prompt ", index3 + 1), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeletePrompt(index3), className: "h-7 w-7 text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__namespace.default.createElement(
98991
98992
  SimpleMarkdownEditor,
98992
98993
  {
98993
98994
  value: promptItem.content,
@@ -99077,7 +99078,7 @@ var DragAndDropQuestionForm = ({ question: question2, onFormChange }) => {
99077
99078
  }
99078
99079
  return htmlString.replace(/<[^>]*>?/gm, "");
99079
99080
  };
99080
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Drag and Drop Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `dnd-bgimage-${question2.id}` }, "Background Image URL (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(
99081
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Drag and Drop Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `dnd-bgimage-${question2.id}` }, "Background Image URL (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(
99081
99082
  Input,
99082
99083
  {
99083
99084
  id: `dnd-bgimage-${question2.id}`,
@@ -99164,7 +99165,7 @@ var HotspotQuestionForm = ({ question: question2, onFormChange }) => {
99164
99165
  }
99165
99166
  onFormChange({ correctHotspotIds: newCorrectIds });
99166
99167
  };
99167
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Hotspot Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `hs-imageurl-${question2.id}` }, "Image URL"), /* @__PURE__ */ React169__namespace.default.createElement(
99168
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Hotspot Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `hs-imageurl-${question2.id}` }, "Image URL"), /* @__PURE__ */ React169__namespace.default.createElement(
99168
99169
  Input,
99169
99170
  {
99170
99171
  id: `hs-imageurl-${question2.id}`,
@@ -99182,7 +99183,7 @@ var HotspotQuestionForm = ({ question: question2, onFormChange }) => {
99182
99183
  onChange: (e3) => onFormChange({ imageAltText: e3.target.value || void 0 }),
99183
99184
  placeholder: "Describe the image"
99184
99185
  }
99185
- )), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Hotspot Areas"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { key: hotspot.id, className: "p-3 border rounded-md bg-background space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-medium" }, "Hotspot ", index3 + 1, " (ID: ", hotspot.id, ")"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeleteHotspot(index3), className: "h-8 w-8 text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `hs-shape-${hotspot.id}`, className: "text-xs" }, "Shape"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: hotspot.shape, onValueChange: (value) => handleHotspotChange(index3, "shape", value) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: `hs-shape-${hotspot.id}` }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "rect" }, "Rectangle"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "circle" }, "Circle")))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(
99186
+ )), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Hotspot Areas"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { key: hotspot.id, className: "p-3 border rounded-md bg-background space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-Medium" }, "Hotspot ", index3 + 1, " (ID: ", hotspot.id, ")"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeleteHotspot(index3), className: "h-8 w-8 text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `hs-shape-${hotspot.id}`, className: "text-xs" }, "Shape"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: hotspot.shape, onValueChange: (value) => handleHotspotChange(index3, "shape", value) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: `hs-shape-${hotspot.id}` }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "rect" }, "Rectangle"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "circle" }, "Circle")))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(
99186
99187
  Input,
99187
99188
  {
99188
99189
  id: `hs-coords-${hotspot.id}`,
@@ -99214,7 +99215,7 @@ var BlocklyProgrammingQuestionForm = ({ question: question2, onFormChange }) =>
99214
99215
  const handleFieldChange = (field, value) => {
99215
99216
  onFormChange({ [field]: value });
99216
99217
  };
99217
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Blockly Programming Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `blockly-toolbox-${question2.id}` }, "Toolbox Definition (XML)"), /* @__PURE__ */ React169__namespace.default.createElement(
99218
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Blockly Programming Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `blockly-toolbox-${question2.id}` }, "Toolbox Definition (XML)"), /* @__PURE__ */ React169__namespace.default.createElement(
99218
99219
  Textarea,
99219
99220
  {
99220
99221
  id: `blockly-toolbox-${question2.id}`,
@@ -99263,7 +99264,7 @@ var ScratchProgrammingQuestionForm = ({ question: question2, onFormChange }) =>
99263
99264
  const handleFieldChange = (field, value) => {
99264
99265
  onFormChange({ [field]: value });
99265
99266
  };
99266
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Scratch Programming Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "scratch-toolbox-" + question2.id }, "Toolbox Definition (XML for Blockly)"), /* @__PURE__ */ React169__namespace.default.createElement(
99267
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Scratch Programming Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "scratch-toolbox-" + question2.id }, "Toolbox Definition (XML for Blockly)"), /* @__PURE__ */ React169__namespace.default.createElement(
99267
99268
  Textarea,
99268
99269
  {
99269
99270
  id: "scratch-toolbox-" + question2.id,
@@ -99340,7 +99341,7 @@ var CodingQuestionForm = ({ question: question2, onFormChange }) => {
99340
99341
  const newTestCases = question2.testCases.filter((_2, i2) => i2 !== index3);
99341
99342
  onFormChange({ testCases: newTestCases });
99342
99343
  };
99343
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-medium text-md" }, "Coding Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `coding-lang-${question2.id}` }, "Programming Language"), /* @__PURE__ */ React169__namespace.default.createElement(
99344
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4 border rounded-md bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-Medium text-md" }, "Coding Question Specifics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `coding-lang-${question2.id}` }, "Programming Language"), /* @__PURE__ */ React169__namespace.default.createElement(
99344
99345
  Select2,
99345
99346
  {
99346
99347
  value: question2.codingLanguage,
@@ -99366,7 +99367,7 @@ var CodingQuestionForm = ({ question: question2, onFormChange }) => {
99366
99367
  placeholder: "Enter the complete, correct code solution here.",
99367
99368
  className: "min-h-[200px] font-mono text-xs"
99368
99369
  }
99369
- )), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Test Cases"), question2.testCases.map((tc, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: tc.id, className: "p-3 border rounded-md bg-background space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-medium" }, "Test Case ", index3 + 1), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeleteTestCase(index3), className: "h-8 w-8 text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `tc-input-${tc.id}`, className: "text-xs" }, "Input (JSON Array)"), /* @__PURE__ */ React169__namespace.default.createElement(
99370
+ )), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Test Cases"), question2.testCases.map((tc, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: tc.id, className: "p-3 border rounded-md bg-background space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-Medium" }, "Test Case ", index3 + 1), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => handleDeleteTestCase(index3), className: "h-8 w-8 text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `tc-input-${tc.id}`, className: "text-xs" }, "Input (JSON Array)"), /* @__PURE__ */ React169__namespace.default.createElement(
99370
99371
  Input,
99371
99372
  {
99372
99373
  id: `tc-input-${tc.id}`,
@@ -99534,7 +99535,7 @@ var EditQuestionModal = ({
99534
99535
  if (!isOpen || !editedQuestion) return null;
99535
99536
  return /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => {
99536
99537
  if (!open) onClose();
99537
- } }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-[600px] md:max-w-[800px] lg:max-w-[1000px] max-h-[90vh]" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(DialogDescription2, null, "Configure the details for this question. Current type:", " ", /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-semibold" }, editedQuestion.questionType))), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "max-h-[calc(80vh-150px)] p-1 pr-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "prompt", className: "font-semibold" }, "Question Prompt"), /* @__PURE__ */ React169__namespace.default.createElement(SimpleMarkdownEditor, { value: editedQuestion.prompt, onChange: (htmlContent) => handleSpecificFieldChange({ prompt: htmlContent }) })), renderSpecificForm(), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "points" }, "Points"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "difficulty" }, "Difficulty"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: editedQuestion.difficulty || "medium", onValueChange: (value) => handleSpecificFieldChange({ difficulty: value }) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "easy" }, "Easy"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "medium" }, "Medium"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "hard" }, "Hard"))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "explanation" }, "Explanation (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(SimpleMarkdownEditor, { value: editedQuestion.explanation || "", onChange: (htmlContent) => handleSpecificFieldChange({ explanation: htmlContent }), minHeight: "100px" })), /* @__PURE__ */ React169__namespace.default.createElement("details", { className: "group", open: hasDropdownMetadata }, /* @__PURE__ */ React169__namespace.default.createElement("summary", { className: "cursor-pointer font-semibold text-primary hover:underline" }, "Advanced Metadata (Optional)"), renderMetadataFields()))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, { className: "pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline" }, "Cancel")), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", onClick: handleSaveClick }, /* @__PURE__ */ React169__namespace.default.createElement(Save, { className: "mr-2 h-4 w-4" }), " Save Question"))));
99538
+ } }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-[600px] md:max-w-[800px] lg:max-w-[1000px] max-h-[90vh]" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(DialogDescription2, null, "Configure the details for this question. Current type:", " ", /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-semibold" }, editedQuestion.questionType))), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "max-h-[calc(80vh-150px)] p-1 pr-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6 p-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "prompt", className: "font-semibold" }, "Question Prompt"), /* @__PURE__ */ React169__namespace.default.createElement(SimpleMarkdownEditor, { value: editedQuestion.prompt, onChange: (htmlContent) => handleSpecificFieldChange({ prompt: htmlContent }) })), renderSpecificForm(), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "points" }, "Points"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "difficulty" }, "Difficulty"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: editedQuestion.difficulty || "Medium", onValueChange: (value) => handleSpecificFieldChange({ difficulty: value }) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "Easy" }, "Easy"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "Medium" }, "Medium"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "Hard" }, "Hard"))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "explanation" }, "Explanation (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(SimpleMarkdownEditor, { value: editedQuestion.explanation || "", onChange: (htmlContent) => handleSpecificFieldChange({ explanation: htmlContent }), minHeight: "100px" })), /* @__PURE__ */ React169__namespace.default.createElement("details", { className: "group", open: hasDropdownMetadata }, /* @__PURE__ */ React169__namespace.default.createElement("summary", { className: "cursor-pointer font-semibold text-primary hover:underline" }, "Advanced Metadata (Optional)"), renderMetadataFields()))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, { className: "pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline" }, "Cancel")), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", onClick: handleSaveClick }, /* @__PURE__ */ React169__namespace.default.createElement(Save, { className: "mr-2 h-4 w-4" }), " Save Question"))));
99538
99539
  };
99539
99540
 
99540
99541
  // src/react-ui/components/authoring/AIQuestionGeneratorModal.tsx
@@ -99684,12 +99685,12 @@ var QuizContextSchema = zod.z.object({
99684
99685
  originalSubject: zod.z.string().optional(),
99685
99686
  originalCategory: zod.z.string().optional(),
99686
99687
  originalTopic: zod.z.string().optional(),
99687
- loDescription: zod.z.string().optional().describe("The full description of the learning objective for deep context."),
99688
+ description: zod.z.string().optional().describe("The full description of the learning objective for deep context."),
99688
99689
  gradeBand: zod.z.string().optional()
99689
99690
  });
99690
99691
  var BaseQuestionGenerationClientInputSchema = zod.z.object({
99691
99692
  language: zod.z.string().optional().default("English"),
99692
- difficulty: zod.z.enum(["easy", "medium", "hard"]),
99693
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]),
99693
99694
  quizContext: QuizContextSchema.optional(),
99694
99695
  imageUrl: zod.z.string().url().optional().describe("Optional URL of an image to be used as context.")
99695
99696
  });
@@ -99698,7 +99699,7 @@ var BaseQuestionZodSchema = zod.z.object({
99698
99699
  prompt: zod.z.string().min(1),
99699
99700
  points: zod.z.number().min(0).optional(),
99700
99701
  explanation: zod.z.string().optional(),
99701
- difficulty: zod.z.enum(["easy", "medium", "hard"]).optional(),
99702
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]).optional(),
99702
99703
  topic: zod.z.string().optional(),
99703
99704
  category: zod.z.string().optional(),
99704
99705
  subject: zod.z.string().optional(),
@@ -99794,7 +99795,7 @@ var AITrueFalseOutputFieldsSchema = zod.z.object({
99794
99795
  correctAnswer: zod.z.boolean(),
99795
99796
  explanation: zod.z.string().optional().describe("An explanation of why the statement is true or false, especially important if false."),
99796
99797
  points: zod.z.number().optional().default(10),
99797
- difficulty: zod.z.enum(["easy", "medium", "hard"]).optional(),
99798
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]).optional(),
99798
99799
  topic: zod.z.string().optional(),
99799
99800
  verifiedCategory: zod.z.string().optional()
99800
99801
  // Thêm để xác thực
@@ -99815,7 +99816,7 @@ Previous attempts failed. Ensure the JSON is valid and 'correctAnswer' is a bool
99815
99816
  const misconceptionGuidance = quizContext?.targetMisconception ? `**Target Misconception:** The statement you create MUST be FALSE and based on this common mistake: "${quizContext.targetMisconception}"` : "";
99816
99817
  const contextStrings = [
99817
99818
  `**Required Category:** ${category}`,
99818
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
99819
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
99819
99820
  imageContextInstruction,
99820
99821
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
99821
99822
  misconceptionGuidance,
@@ -99826,7 +99827,7 @@ Previous attempts failed. Ensure the JSON is valid and 'correctAnswer' is a bool
99826
99827
  correctAnswer: true,
99827
99828
  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 '!'.",
99828
99829
  points: 10,
99829
- difficulty: "easy",
99830
+ difficulty: "Easy",
99830
99831
  topic: "Swift Optionals",
99831
99832
  verifiedCategory: category
99832
99833
  }, null, 2);
@@ -99962,7 +99963,7 @@ var AIMCQOutputFieldsSchema = zod.z.object({
99962
99963
  correctTempOptionId: zod.z.string().describe("The temporary ID of the correct option from the generated options array."),
99963
99964
  explanation: zod.z.string().optional().describe("A brief explanation of why the answer is correct."),
99964
99965
  points: zod.z.number().optional().default(10),
99965
- difficulty: zod.z.enum(["easy", "medium", "hard"]).optional(),
99966
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]).optional(),
99966
99967
  topic: zod.z.string().optional(),
99967
99968
  verifiedCategory: zod.z.string().optional().describe("The category this question actually addresses.")
99968
99969
  });
@@ -99981,7 +99982,7 @@ Previous attempts failed...
99981
99982
  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.` : "";
99982
99983
  const contextStrings = [
99983
99984
  `**Required Category:** ${category}`,
99984
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
99985
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
99985
99986
  imageContextInstruction,
99986
99987
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
99987
99988
  quizContext?.targetMisconception && `**Target Misconception:** Use this to create plausible incorrect answers: "${quizContext.targetMisconception}"`,
@@ -99998,7 +99999,7 @@ Previous attempts failed...
99998
99999
  correctTempOptionId: "C",
99999
100000
  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.`,
100000
100001
  points: 10,
100001
- difficulty: "easy",
100002
+ difficulty: "Easy",
100002
100003
  topic: `Control Flow in ${category}`,
100003
100004
  verifiedCategory: category
100004
100005
  }, null, 2);
@@ -100130,807 +100131,6 @@ async function generateMCQQuestion(clientInput, apiKey) {
100130
100131
  return { error: errorMessage };
100131
100132
  }
100132
100133
 
100133
- // src/react-ui/components/authoring/AIQuestionGeneratorModal.tsx
100134
- var supportedQuestionTypesForAI = [
100135
- { value: "true_false", label: "True/False" },
100136
- { value: "multiple_choice", label: "Multiple Choice" },
100137
- { value: "multiple_response", label: "Multiple Response" },
100138
- { value: "short_answer", label: "Short Answer" },
100139
- { value: "numeric", label: "Numeric" },
100140
- { value: "fill_in_the_blanks", label: "Fill In The Blanks" },
100141
- { value: "sequence", label: "Sequence" },
100142
- { value: "matching", label: "Matching" }
100143
- ];
100144
- var AIQuestionGeneratorModal = ({
100145
- isOpen,
100146
- onClose,
100147
- onQuestionGenerated,
100148
- language: language3,
100149
- questionType: questionTypeProp,
100150
- subjects = [],
100151
- topics = [],
100152
- gradeLevels = [],
100153
- bloomLevels = []
100154
- }) => {
100155
- const [prompt, setPrompt] = React169.useState("");
100156
- const [isLoading, setIsLoading] = React169.useState(false);
100157
- const [error, setError] = React169.useState(null);
100158
- const { toast: toast2 } = useToast();
100159
- const [subjectCode, setSubjectCode] = React169.useState("");
100160
- const [topicCode, setTopicCode] = React169.useState("");
100161
- const [gradeBand, setGradeBand] = React169.useState("");
100162
- const [bloomLevelCode, setBloomLevelCode] = React169.useState("");
100163
- const [selectedQuestionType, setSelectedQuestionType] = React169.useState("multiple_choice");
100164
- const [isApiKeyManagerModalOpen, setIsApiKeyManagerModalOpen] = React169.useState(false);
100165
- const [geminiApiKeyExists, setGeminiApiKeyExists] = React169.useState(false);
100166
- const finalQuestionType = questionTypeProp || selectedQuestionType;
100167
- React169.useEffect(() => {
100168
- if (isOpen) {
100169
- setPrompt("");
100170
- setError(null);
100171
- setIsLoading(false);
100172
- setSubjectCode("");
100173
- setTopicCode("");
100174
- setGradeBand("");
100175
- setBloomLevelCode("");
100176
- setSelectedQuestionType(questionTypeProp || "multiple_choice");
100177
- setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
100178
- }
100179
- }, [isOpen, questionTypeProp]);
100180
- const filteredTopics = React169.useMemo(() => {
100181
- if (!subjectCode) return [];
100182
- return topics.filter((t4) => t4.subjectCode === subjectCode);
100183
- }, [subjectCode, topics]);
100184
- const handleApiKeyModalClose = () => {
100185
- setIsApiKeyManagerModalOpen(false);
100186
- setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
100187
- };
100188
- const handleSubmit = async () => {
100189
- if (!prompt.trim()) {
100190
- setError("Please provide a prompt for the question.");
100191
- return;
100192
- }
100193
- const geminiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
100194
- if (!geminiKey) {
100195
- setError("Gemini API Key is not set. Please configure it.");
100196
- setGeminiApiKeyExists(false);
100197
- return;
100198
- }
100199
- setGeminiApiKeyExists(true);
100200
- setError(null);
100201
- setIsLoading(true);
100202
- try {
100203
- const quizContext = {
100204
- plannedTopic: prompt,
100205
- originalSubject: subjectCode,
100206
- originalTopic: topicCode,
100207
- gradeBand,
100208
- plannedBloomLevel: bloomLevelCode
100209
- };
100210
- const baseClientInput = {
100211
- language: language3,
100212
- difficulty: "medium",
100213
- quizContext
100214
- };
100215
- let generatedResult = {};
100216
- switch (finalQuestionType) {
100217
- case "true_false":
100218
- generatedResult = await generateTrueFalseQuestion(baseClientInput, geminiKey);
100219
- break;
100220
- case "multiple_choice":
100221
- generatedResult = await generateMCQQuestion({ ...baseClientInput, numberOfOptions: 4 }, geminiKey);
100222
- break;
100223
- // Add other cases as needed
100224
- default:
100225
- throw new Error(`AI generation for '${finalQuestionType}' is not implemented in this flow.`);
100226
- }
100227
- if (generatedResult.error) {
100228
- throw new Error(generatedResult.error);
100229
- }
100230
- if (generatedResult.question) {
100231
- const completeQuestion = generatedResult.question;
100232
- completeQuestion.subject = subjectCode;
100233
- completeQuestion.topic = topicCode;
100234
- completeQuestion.gradeBand = gradeBand;
100235
- completeQuestion.bloomLevel = bloomLevelCode;
100236
- if (completeQuestion.points === void 0) completeQuestion.points = 10;
100237
- onQuestionGenerated(completeQuestion);
100238
- toast2({ title: "AI Question Generated", description: "Review and edit the generated question." });
100239
- onClose();
100240
- } else {
100241
- throw new Error("AI did not return a valid question object.");
100242
- }
100243
- } catch (e3) {
100244
- console.error("AI Question Generation Error:", e3);
100245
- let errorMessage = e3.message || "An unknown error occurred.";
100246
- if (typeof errorMessage === "string" && errorMessage.includes("API key not valid")) {
100247
- errorMessage = "The provided Google Gemini API Key is invalid. Please check it.";
100248
- setGeminiApiKeyExists(false);
100249
- }
100250
- setError(errorMessage);
100251
- toast2({ title: "AI Generation Failed", description: errorMessage, variant: "destructive" });
100252
- } finally {
100253
- setIsLoading(false);
100254
- }
100255
- };
100256
- const comboboxOptions = {
100257
- subjects: subjects.map((s4) => ({ value: s4.code, label: s4.name })),
100258
- topics: filteredTopics.map((t4) => ({ value: t4.code, label: t4.name })),
100259
- gradeLevels: gradeLevels.map((g) => ({ value: g.code, label: g.name })),
100260
- bloomLevels: bloomLevels.map((b2) => ({ value: b2.code, label: b2.name }))
100261
- };
100262
- return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => {
100263
- if (!open) onClose();
100264
- } }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-[600px]" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, { className: "flex items-center font-headline text-2xl" }, /* @__PURE__ */ React169__namespace.default.createElement(WandSparkles, { className: "mr-2 h-6 w-6 text-primary" }), " AI Question Generator"), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, "Provide a prompt and metadata to generate a '", finalQuestionType, "' question.")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 py-4 max-h-[60vh] overflow-y-auto px-2" }, !geminiApiKeyExists && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-3 mb-4 border border-amber-500 bg-amber-50 rounded-md text-amber-700" }), !questionTypeProp && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-q-type-select" }, "Question Type"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedQuestionType, onValueChange: (v) => setSelectedQuestionType(v) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "ai-q-type-select" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, supportedQuestionTypesForAI.map((type) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: type.value, value: type.value }, type.label))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-prompt" }, "Prompt / Topic"), /* @__PURE__ */ React169__namespace.default.createElement(
100265
- Textarea,
100266
- {
100267
- id: "ai-prompt",
100268
- value: prompt,
100269
- onChange: (e3) => setPrompt(e3.target.value),
100270
- placeholder: "e.g., The process of photosynthesis, The causes of World War II, Basic Algebra Equations",
100271
- className: "min-h-[100px]"
100272
- }
100273
- )), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.subjects, value: subjectCode, onChange: setSubjectCode, placeholder: "Select or type a Subject..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Topic"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.topics, value: topicCode, onChange: setTopicCode, placeholder: "Select or type a Topic...", disabled: !subjectCode })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Grade Level"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.gradeLevels, value: gradeBand, onChange: setGradeBand, placeholder: "Select or type a Grade..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Bloom's Level"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.bloomLevels, value: bloomLevelCode, onChange: setBloomLevelCode, placeholder: "Select a Bloom's Level..." }))), error && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error)), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline" }, "Cancel")), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", onClick: handleSubmit, disabled: isLoading || !prompt.trim() || !geminiApiKeyExists }, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__namespace.default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), "Generate Question")))), /* @__PURE__ */ React169__namespace.default.createElement(APIKeyManagerModal, { isOpen: isApiKeyManagerModalOpen, onClose: handleApiKeyModalClose }));
100274
- };
100275
-
100276
- // src/react-ui/components/authoring/AIFullQuizGeneratorModal.tsx
100277
- init_react_shim();
100278
-
100279
- // src/ai/flows/generate-quiz-plan.ts
100280
- init_react_shim();
100281
-
100282
- // src/ai/flows/generate-quiz-plan-types.ts
100283
- init_react_shim();
100284
- var TopicWithMetadataSchema = zod.z.object({
100285
- topic: zod.z.string().min(1),
100286
- ratio: zod.z.number().min(0).max(100),
100287
- originalLoId: zod.z.string().optional(),
100288
- originalSubject: zod.z.string().optional(),
100289
- originalCategory: zod.z.string().optional(),
100290
- originalTopic: zod.z.string().optional(),
100291
- commonMisconceptions: zod.z.array(zod.z.string()).optional()
100292
- });
100293
- var BloomLevelStringsEnum = zod.z.enum(["remembering", "understanding", "applying", "analyzing", "evaluating", "creating"]);
100294
- var fullQuizSupportedQuestionTypesArray = [
100295
- "true_false",
100296
- "multiple_choice",
100297
- "multiple_response",
100298
- "short_answer",
100299
- "numeric",
100300
- "fill_in_the_blanks",
100301
- "sequence",
100302
- "matching",
100303
- "drag_and_drop",
100304
- "coding"
100305
- ];
100306
- zod.z.object({
100307
- language: zod.z.string().optional().default("English"),
100308
- totalQuestions: zod.z.number().int().min(1).max(50),
100309
- numCodingQuestions: zod.z.number().optional().default(0),
100310
- topics: zod.z.array(TopicWithMetadataSchema).min(1),
100311
- bloomLevels: zod.z.array(zod.z.object({
100312
- level: BloomLevelStringsEnum,
100313
- ratio: zod.z.number().min(0).max(100)
100314
- })).min(1),
100315
- selectedContextIds: zod.z.array(zod.z.string()).optional(),
100316
- selectedQuestionTypes: zod.z.array(zod.z.enum(fullQuizSupportedQuestionTypesArray)).min(1),
100317
- imageContexts: zod.z.array(zod.z.custom()).optional().describe("Library of available image contexts for the AI to use.")
100318
- });
100319
- var PlannedQuestionSchema = zod.z.object({
100320
- plannedTopic: zod.z.string().min(1).describe("The specific, assessable topic for this question."),
100321
- plannedQuestionType: zod.z.enum(fullQuizSupportedQuestionTypesArray).describe("The specific question type chosen."),
100322
- plannedBloomLevel: BloomLevelStringsEnum.describe("The Bloom's level assigned."),
100323
- plannedContextId: zod.z.string().optional().describe("The specific context ID chosen for this question."),
100324
- imageId: zod.z.string().nullable().optional().describe("The ID of the image from the context library to be used for this question."),
100325
- targetMisconception: zod.z.string().optional().describe("A specific common misconception this question should target."),
100326
- difficultyReason: zod.z.string().optional().describe("Strategic explanation of difficulty choice and placement."),
100327
- topicSpecificity: zod.z.enum(["broad", "focused", "specific"]).optional().describe("How specific the topic coverage should be."),
100328
- originalLoId: zod.z.string().optional(),
100329
- originalSubject: zod.z.string().optional(),
100330
- originalCategory: zod.z.string().optional(),
100331
- originalTopic: zod.z.string().optional()
100332
- });
100333
- var GenerateQuizPlanOutputSchema = zod.z.object({
100334
- quizPlan: zod.z.array(PlannedQuestionSchema).describe("A detailed plan for each question in the quiz."),
100335
- diversityMetrics: zod.z.object({
100336
- questionTypeDistribution: zod.z.record(zod.z.number()).optional(),
100337
- bloomLevelDistribution: zod.z.record(zod.z.number()).optional(),
100338
- maxConsecutiveSameType: zod.z.number().optional()
100339
- }).optional().describe("Metrics showing the diversity achieved in the plan."),
100340
- planningStrategy: zod.z.object({
100341
- overallApproach: zod.z.string().optional(),
100342
- keyDecisions: zod.z.array(zod.z.string()).optional()
100343
- }).optional()
100344
- });
100345
-
100346
- // src/ai/flows/generate-quiz-plan.ts
100347
- var QuizPlanLogger = class {
100348
- constructor() {
100349
- this.logs = [];
100350
- this.startTime = Date.now();
100351
- }
100352
- log(phase, data, duration) {
100353
- this.logs.push({
100354
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
100355
- phase,
100356
- data,
100357
- duration
100358
- });
100359
- if (duration !== void 0) {
100360
- console.log(`[${phase}] Completed in ${duration}ms:`, data);
100361
- } else {
100362
- console.log(`[${phase}]:`, data);
100363
- }
100364
- }
100365
- getLogs() {
100366
- return this.logs;
100367
- }
100368
- getTotalDuration() {
100369
- return Date.now() - this.startTime;
100370
- }
100371
- };
100372
- function generateQuestionTypeSelectionGuidance() {
100373
- return `
100374
- QUESTION TYPE SELECTION BEST PRACTICES:
100375
-
100376
- **TRUE_FALSE (true_false)**
100377
- - Best for: Binary concepts, fact verification, common misconceptions
100378
- - Bloom levels: Primarily Remembering, Understanding
100379
- - Use when: Testing definitive statements, clarifying misconceptions
100380
- - Example: "Photosynthesis only occurs during daytime" (targets timing misconception)
100381
-
100382
- **MULTIPLE CHOICE (multiple_choice)**
100383
- - Best for: Concept selection, process understanding, comparison
100384
- - Bloom levels: All levels, especially Understanding and Applying
100385
- - Use when: Testing conceptual understanding with clear alternatives
100386
- - Example: "Which factor most affects enzyme activity?" (applying knowledge)
100387
-
100388
- **MULTIPLE RESPONSE (multiple_response)**
100389
- - Best for: Identifying multiple correct factors, comprehensive understanding
100390
- - Bloom levels: Understanding, Analyzing, Evaluating
100391
- - Use when: Multiple correct answers exist, testing thorough knowledge
100392
- - Example: "Select all factors that influence plant growth" (analyzing components)
100393
-
100394
- **FILL IN THE BLANKS (fill_in_the_blanks)**
100395
- - Best for: Key terminology, formulas, specific facts
100396
- - Bloom levels: Remembering, Understanding
100397
- - Use when: Testing precise recall of important terms/concepts
100398
- - Example: "The process of _____ converts light energy to chemical energy"
100399
-
100400
- **NUMERIC (numeric)**
100401
- - Best for: Calculations, quantitative problems, formula application
100402
- - Bloom levels: Applying, Analyzing
100403
- - Use when: Mathematical computations are required
100404
- - Example: "Calculate the molarity of a 2L solution containing 0.5 moles of NaCl"
100405
-
100406
- **MATCHING (matching)**
100407
- - Best for: Connecting related concepts, terminology pairs
100408
- - Bloom levels: Remembering, Understanding
100409
- - Use when: Testing relationships between terms and definitions
100410
- - Example: Match organelles with their functions
100411
-
100412
- **SEQUENCE (sequence)**
100413
- - Best for: Process steps, chronological order, procedural knowledge
100414
- - Bloom levels: Understanding, Applying, Analyzing
100415
- - Use when: Order or sequence is critical to understanding
100416
- - Example: "Arrange the steps of mitosis in correct order"
100417
-
100418
- **DRAG AND DROP (drag_and_drop)**
100419
- - Best for: Categorization, classification, spatial relationships
100420
- - Bloom levels: Understanding, Applying, Analyzing
100421
- - Use when: Grouping or organizing information is key
100422
- - Example: "Classify these compounds as acids, bases, or neutral"
100423
-
100424
- **SHORT ANSWER (short_answer)**
100425
- - Best for: Explanations, definitions, problem-solving steps
100426
- - Bloom levels: Understanding, Applying, Analyzing, Evaluating, Creating
100427
- - Use when: Requiring explanatory responses, open-ended thinking
100428
- - Example: "Explain why enzymes are specific to certain substrates"
100429
-
100430
- **CODING (coding)**
100431
- - Best for: Programming problems, algorithm implementation
100432
- - Bloom levels: Applying, Analyzing, Evaluating, Creating
100433
- - Use when: Technical implementation or code analysis is required
100434
- - Example: "Write a function to calculate factorial recursively"
100435
- `;
100436
- }
100437
- function generateAdvancedBloomGuidance() {
100438
- return `
100439
- ADVANCED BLOOM'S TAXONOMY & QUESTION TYPE OPTIMIZATION:
100440
-
100441
- **REMEMBERING (Knowledge Recall)**
100442
- - Primary types: true_false, fill_in_the_blanks, matching
100443
- - Secondary types: multiple_choice (simple recall)
100444
- - Focus: Facts, terms, basic concepts, definitions
100445
- - Misconception addressing: Use true_false to clarify common confusions
100446
-
100447
- **UNDERSTANDING (Comprehension)**
100448
- - Primary types: multiple_choice, short_answer, matching
100449
- - Secondary types: multiple_response, sequence
100450
- - Focus: Explanations, interpretations, examples, classifications
100451
- - Best for: "Explain why...", "What does this mean...", "Give an example..."
100452
-
100453
- **APPLYING (Using Knowledge)**
100454
- - Primary types: numeric, short_answer, sequence, coding
100455
- - Secondary types: multiple_choice, drag_and_drop
100456
- - Focus: Problem-solving, implementing procedures, using methods
100457
- - Best for: Calculations, step-by-step processes, practical applications
100458
-
100459
- **ANALYZING (Breaking Down Information)**
100460
- - Primary types: multiple_response, short_answer, sequence, coding
100461
- - Secondary types: drag_and_drop, multiple_choice
100462
- - Focus: Identifying components, relationships, cause-effect
100463
- - Best for: "What are the factors...", "How do these relate...", "Break down..."
100464
-
100465
- **EVALUATING (Making Judgments)**
100466
- - Primary types: short_answer, multiple_response, coding
100467
- - Secondary types: multiple_choice (with justification)
100468
- - Focus: Critiquing, judging, comparing alternatives, decision-making
100469
- - Best for: "Which is better and why...", "Evaluate the approach...", "Critique..."
100470
-
100471
- **CREATING (Producing New Work)**
100472
- - Primary types: short_answer, coding, sequence
100473
- - Secondary types: drag_and_drop (design tasks)
100474
- - Focus: Designing, planning, producing, constructing
100475
- - Best for: "Design a solution...", "Create a plan...", "Develop a strategy..."
100476
- `;
100477
- }
100478
- function generateDiversityRules() {
100479
- return `
100480
- ENHANCED DIVERSITY & QUALITY ASSURANCE RULES:
100481
-
100482
- **Question Type Distribution Strategy:**
100483
- 1. Never place more than 3 consecutive questions of the same type
100484
- 2. Distribute question types based on their cognitive complexity
100485
- 3. For quizzes with 10+ questions, use at least 4 different question types
100486
- 4. For quizzes with 20+ questions, use at least 6 different question types
100487
- 5. Balance quick-answer types (true_false, multiple_choice) with deeper types (short_answer, coding)
100488
-
100489
- **Intelligent Difficulty Progression:**
100490
- 1. Opening (20%): Start with confidence-building questions (Remembering/Understanding)
100491
- 2. Building (40%): Gradually increase complexity (Understanding/Applying)
100492
- 3. Peak (30%): Most challenging questions (Analyzing/Evaluating/Creating)
100493
- 4. Closing (10%): Moderate challenge to end positively
100494
-
100495
- **Misconception-Driven Planning:**
100496
- - When common misconceptions are provided, prioritize question types that can effectively address them
100497
- - Use true_false for binary misconceptions
100498
- - Use multiple_choice for concept selection misconceptions
100499
- - Use short_answer for complex misconceptions requiring explanation
100500
-
100501
- **Contextual Intelligence:**
100502
- - Programming/Technical topics: Favor coding, numeric, short_answer
100503
- - Process-oriented topics: Favor sequence, short_answer, drag_and_drop
100504
- - Conceptual topics: Favor multiple_choice, multiple_response, true_false
100505
- - Factual topics: Favor fill_in_the_blanks, matching, true_false
100506
- `;
100507
- }
100508
- async function generateQuizPlan(clientInput, apiKey, imageContexts = []) {
100509
- const logger = new QuizPlanLogger();
100510
- try {
100511
- logger.log("VALIDATION_START", {
100512
- totalQuestions: clientInput.totalQuestions,
100513
- availableTypes: clientInput.selectedQuestionTypes,
100514
- topicCount: clientInput.topics.length,
100515
- bloomLevelCount: clientInput.bloomLevels.length
100516
- });
100517
- const totalTopicRatio = clientInput.topics.reduce((sum, t4) => sum + t4.ratio, 0);
100518
- if (Math.abs(totalTopicRatio - 100) > 1) {
100519
- throw new Error(`Total topic ratio must be 100%. Current sum: ${totalTopicRatio.toFixed(1)}%`);
100520
- }
100521
- const totalBloomRatio = clientInput.bloomLevels.reduce((sum, b2) => sum + b2.ratio, 0);
100522
- if (Math.abs(totalBloomRatio - 100) > 1) {
100523
- throw new Error(`Total Bloom level ratio must be 100%. Current sum: ${totalBloomRatio.toFixed(1)}%`);
100524
- }
100525
- logger.log("VALIDATION_SUCCESS", {
100526
- topicRatioSum: totalTopicRatio,
100527
- bloomRatioSum: totalBloomRatio
100528
- });
100529
- const aiStartTime = Date.now();
100530
- const ai = new genai.GoogleGenAI({
100531
- apiKey
100532
- });
100533
- const model = "gemini-2.5-pro";
100534
- const config3 = {
100535
- temperature: 0.8,
100536
- thinkingConfig: {
100537
- thinkingBudget: 4096
100538
- },
100539
- responseMimeType: "application/json"
100540
- };
100541
- logger.log("AI_INITIALIZATION", { model }, Date.now() - aiStartTime);
100542
- const { language: language3, totalQuestions, numCodingQuestions = 0 } = clientInput;
100543
- const promptStartTime = Date.now();
100544
- const topicsDistribution = clientInput.topics.map((t4) => {
100545
- 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}%`;
100546
- if (t4.commonMisconceptions && t4.commonMisconceptions.length > 0) {
100547
- topicString += `
100548
- - Common Misconceptions: [${t4.commonMisconceptions.join(", ")}]`;
100549
- }
100550
- return topicString;
100551
- }).join("\n ");
100552
- const bloomDistribution = clientInput.bloomLevels.map(
100553
- (b2) => `- Level: "${b2.level}", Ratio: ${b2.ratio}%`
100554
- ).join("\n ");
100555
- let questionTypesForPrompt = [...clientInput.selectedQuestionTypes];
100556
- if (numCodingQuestions === 0) {
100557
- questionTypesForPrompt = questionTypesForPrompt.filter(
100558
- (type) => type !== "short_answer" && type !== "coding"
100559
- );
100560
- }
100561
- const allowedQuestionTypes = questionTypesForPrompt.map((t4) => `'${t4}'`).join(", ");
100562
- const codingRequirement = numCodingQuestions > 0 ? `
100563
- **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.` : "";
100564
- const imageContextSection = imageContexts && imageContexts.length > 0 ? `
100565
- ## AVAILABLE IMAGE CONTEXT LIBRARY
100566
- 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.
100567
-
100568
- \`\`\`json
100569
- ${JSON.stringify(imageContexts.map((img) => ({ imageId: img.id, subject: img.subject, category: img.category, topic: img.topic, description: img.detailedDescription })), null, 2)}
100570
- \`\`\`
100571
- ` : "";
100572
- 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.
100573
-
100574
- ${generateQuestionTypeSelectionGuidance()}
100575
-
100576
- ${generateAdvancedBloomGuidance()}
100577
-
100578
- ${generateDiversityRules()}
100579
-
100580
- ## COMPREHENSIVE QUIZ REQUIREMENTS:
100581
-
100582
- **Target Language**: ${language3}
100583
- **Total Questions**: ${totalQuestions}${codingRequirement}
100584
-
100585
- **Topic Distribution & Learning Context** (follow precisely):
100586
- ${topicsDistribution}
100587
-
100588
- **Cognitive Complexity Distribution** (follow precisely):
100589
- ${bloomDistribution}
100590
-
100591
- **Available Question Arsenal**: ${allowedQuestionTypes}
100592
-
100593
- **Image Resources**: ${imageContextSection}
100594
-
100595
- ## STRATEGIC PLANNING METHODOLOGY:
100596
-
100597
- 1. **Misconception Analysis**: If common misconceptions are provided, design questions specifically to address and correct them
100598
- 2. **Question Type Intelligence**: Select question types based on the cognitive demands and content nature
100599
- 3. **Difficulty Orchestration**: Create a learning journey that builds confidence while challenging appropriately
100600
- 4. **Diversity Optimization**: Ensure variety prevents monotony and maintains engagement
100601
- 5. **Context Sensitivity**: Match question types to topic characteristics (technical, conceptual, procedural)
100602
- 6. **Image Context Integration**: Intelligently associate questions with relevant images from the provided library to enhance contextual understanding.
100603
-
100604
- ## ENHANCED OUTPUT FORMAT:
100605
-
100606
- Return ONLY a JSON object with this EXACT structure:
100607
-
100608
- \`\`\`json
100609
- {
100610
- "quizPlan": [
100611
- {
100612
- "plannedTopic": "Specific, assessable topic derived from provided context",
100613
- "plannedQuestionType": "question_type_from_allowed_list",
100614
- "plannedBloomLevel": "bloom_level_from_requirements",
100615
- "plannedContextId": "THEO_ABS",
100616
- "imageId": "imgctx_12345abcde", // or null
100617
- "targetMisconception": "Specific misconception this question addresses (or 'none' if not applicable)",
100618
- "difficultyReason": "Strategic explanation of difficulty choice and placement",
100619
- "topicSpecificity": "broad|focused|specific",
100620
- "originalLoId": "corresponding_LoId_from_input",
100621
- "originalSubject": "corresponding_Subject_from_input",
100622
- "originalCategory": "corresponding_Category_from_input",
100623
- "originalTopic": "corresponding_Topic_from_input"
100624
- }
100625
- ],
100626
- "diversityMetrics": {
100627
- "questionTypeDistribution": {"type1": count1, "type2": count2},
100628
- "bloomLevelDistribution": {"level1": count1, "level2": count2},
100629
- "maxConsecutiveSameType": number,
100630
- "difficultyProgression": "description of how difficulty progresses",
100631
- "misconceptionCoverage": number_of_misconceptions_addressed
100632
- },
100633
- "planningStrategy": {
100634
- "overallApproach": "Brief description of the strategic approach taken",
100635
- "keyDecisions": ["Major decision 1", "Major decision 2", "Major decision 3"]
100636
- }
100637
- }
100638
- \`\`\`
100639
-
100640
- Execute this plan with pedagogical precision. The quiz should feel like a carefully crafted learning journey that challenges and educates simultaneously.`;
100641
- logger.log("PROMPT_PREPARATION", {
100642
- promptLength: enhancedPromptText.length,
100643
- topicCount: clientInput.topics.length,
100644
- misconceptionCount: clientInput.topics.reduce((sum, t4) => sum + (t4.commonMisconceptions?.length || 0), 0)
100645
- }, Date.now() - promptStartTime);
100646
- const generationStartTime = Date.now();
100647
- const contents = [
100648
- {
100649
- role: "user",
100650
- parts: [
100651
- {
100652
- text: enhancedPromptText
100653
- }
100654
- ]
100655
- }
100656
- ];
100657
- const aiResult = await ai.models.generateContent({
100658
- model,
100659
- config: config3,
100660
- contents
100661
- });
100662
- const response = aiResult;
100663
- const generationDuration = Date.now() - generationStartTime;
100664
- logger.log("AI_GENERATION", {
100665
- responseLength: response.candidates?.[0]?.content?.parts?.[0]?.text?.length || 0,
100666
- duration: generationDuration
100667
- }, generationDuration);
100668
- const processingStartTime = Date.now();
100669
- const rawText = response.candidates?.[0]?.content?.parts?.[0]?.text || "";
100670
- let jsonText = rawText;
100671
- if (!rawText.trim().startsWith("{") && !rawText.trim().startsWith("[")) {
100672
- jsonText = extractJsonFromMarkdown(rawText);
100673
- }
100674
- logger.log("JSON_EXTRACTION", {
100675
- rawTextLength: rawText.length,
100676
- extractedJsonLength: jsonText.length
100677
- });
100678
- const aiGeneratedContent = GenerateQuizPlanOutputSchema.parse(JSON.parse(jsonText));
100679
- logger.log("SCHEMA_VALIDATION", { success: true }, Date.now() - processingStartTime);
100680
- const validationStartTime = Date.now();
100681
- if (aiGeneratedContent.quizPlan.length !== clientInput.totalQuestions) {
100682
- throw new Error(`AI planned for ${aiGeneratedContent.quizPlan.length} questions, but ${clientInput.totalQuestions} were requested.`);
100683
- }
100684
- const invalidTypes = [];
100685
- aiGeneratedContent.quizPlan.forEach((item, index3) => {
100686
- if (!clientInput.selectedQuestionTypes.includes(item.plannedQuestionType)) {
100687
- invalidTypes.push(`Question ${index3 + 1}: '${item.plannedQuestionType}'`);
100688
- }
100689
- });
100690
- if (invalidTypes.length > 0) {
100691
- throw new Error(`Invalid question types found: ${invalidTypes.join(", ")}`);
100692
- }
100693
- const codingQuestions = aiGeneratedContent.quizPlan.filter((q2) => q2.plannedQuestionType === "coding");
100694
- if (numCodingQuestions > 0 && codingQuestions.length !== numCodingQuestions) {
100695
- throw new Error(`Expected ${numCodingQuestions} coding questions, but got ${codingQuestions.length}`);
100696
- }
100697
- const diversityAnalysis = validateConsecutiveTypes(aiGeneratedContent.quizPlan);
100698
- logger.log("VALIDATION_COMPLETE", {
100699
- questionCount: aiGeneratedContent.quizPlan.length,
100700
- codingQuestionCount: codingQuestions.length,
100701
- maxConsecutiveType: diversityAnalysis.maxConsecutive,
100702
- questionTypeDistribution: aiGeneratedContent.diversityMetrics?.questionTypeDistribution || {}
100703
- }, Date.now() - validationStartTime);
100704
- const finalResult = {
100705
- ...aiGeneratedContent,
100706
- logs: logger.getLogs()
100707
- };
100708
- logger.log("GENERATION_COMPLETE", {
100709
- totalDuration: logger.getTotalDuration(),
100710
- success: true,
100711
- finalQuestionCount: finalResult.quizPlan.length
100712
- }, logger.getTotalDuration());
100713
- console.log("\n=== QUIZ PLAN GENERATION SUMMARY ===");
100714
- console.log(`\u2705 Successfully generated ${finalResult.quizPlan.length} questions`);
100715
- console.log(`\u23F1\uFE0F Total generation time: ${logger.getTotalDuration()}ms`);
100716
- console.log(`\u{1F3AF} Question types: ${Object.keys(finalResult.diversityMetrics?.questionTypeDistribution || {}).join(", ")}`);
100717
- console.log(`\u{1F9E0} Bloom levels: ${Object.keys(finalResult.diversityMetrics?.bloomLevelDistribution || {}).join(", ")}`);
100718
- if (numCodingQuestions > 0) {
100719
- console.log(`\u{1F4BB} Coding questions: ${codingQuestions.length}/${numCodingQuestions} required`);
100720
- }
100721
- console.log(JSON.stringify(finalResult));
100722
- console.log("=====================================\n");
100723
- return finalResult;
100724
- } catch (error) {
100725
- logger.log("ERROR", {
100726
- message: error.message,
100727
- stack: error.stack,
100728
- totalDuration: logger.getTotalDuration()
100729
- });
100730
- console.error("\u274C Quiz Plan Generation Failed:", error.message);
100731
- console.error("\u{1F4CB} Full logs available in returned object");
100732
- throw new Error(`Failed to generate Quiz Plan: ${error.message}`);
100733
- }
100734
- }
100735
- function validateConsecutiveTypes(quizPlan) {
100736
- let maxConsecutive = 1;
100737
- let maxType = quizPlan[0]?.plannedQuestionType || "";
100738
- let maxStartIndex = 0;
100739
- let currentConsecutive = 1;
100740
- let currentType = quizPlan[0]?.plannedQuestionType || "";
100741
- let currentStartIndex = 0;
100742
- for (let i2 = 1; i2 < quizPlan.length; i2++) {
100743
- if (quizPlan[i2].plannedQuestionType === currentType) {
100744
- currentConsecutive++;
100745
- } else {
100746
- if (currentConsecutive > maxConsecutive) {
100747
- maxConsecutive = currentConsecutive;
100748
- maxType = currentType;
100749
- maxStartIndex = currentStartIndex;
100750
- }
100751
- currentConsecutive = 1;
100752
- currentType = quizPlan[i2].plannedQuestionType;
100753
- currentStartIndex = i2;
100754
- }
100755
- }
100756
- if (currentConsecutive > maxConsecutive) {
100757
- maxConsecutive = currentConsecutive;
100758
- maxType = currentType;
100759
- maxStartIndex = currentStartIndex;
100760
- }
100761
- return {
100762
- maxConsecutive,
100763
- type: maxType,
100764
- startIndex: maxStartIndex
100765
- };
100766
- }
100767
-
100768
- // src/ai/flows/generate-questions-from-quiz-plan.ts
100769
- init_react_shim();
100770
-
100771
- // src/services/TopicDataService.ts
100772
- init_react_shim();
100773
- var TopicDataService = class {
100774
- /**
100775
- * Saves an array of LearningObjective objects to Local Storage, overwriting existing data.
100776
- * @param data The array of learning objectives to save.
100777
- */
100778
- static saveData(data) {
100779
- try {
100780
- if (typeof window === "undefined") return;
100781
- const serializedData = JSON.stringify(data);
100782
- localStorage.setItem(this.STORAGE_KEY, serializedData);
100783
- } catch (error) {
100784
- console.error("Error saving learning objectives to Local Storage:", error);
100785
- }
100786
- }
100787
- /**
100788
- * Merges a new set of learning objectives with the existing data in Local Storage.
100789
- * If an LO ID from newData already exists, it will be updated. Otherwise, it will be added.
100790
- * @param newData The array of new or updated learning objectives.
100791
- */
100792
- static mergeData(newData) {
100793
- const existingData = this.getData();
100794
- const loMap = new Map(existingData.map((lo) => [lo.loId, lo]));
100795
- newData.forEach((newLo) => {
100796
- loMap.set(newLo.loId, newLo);
100797
- });
100798
- const mergedData = Array.from(loMap.values());
100799
- this.saveData(mergedData);
100800
- }
100801
- /**
100802
- * Retrieves the array of LearningObjective objects from Local Storage.
100803
- * @returns An array of learning objectives, or an empty array if none are found or an error occurs.
100804
- */
100805
- static getData() {
100806
- try {
100807
- if (typeof window === "undefined") return [];
100808
- const storedData = localStorage.getItem(this.STORAGE_KEY);
100809
- return storedData ? JSON.parse(storedData) : [];
100810
- } catch (error) {
100811
- console.error("Error retrieving learning objectives from Local Storage:", error);
100812
- this.clearData();
100813
- return [];
100814
- }
100815
- }
100816
- /**
100817
- * Removes all learning objective data from Local Storage.
100818
- */
100819
- static clearData() {
100820
- try {
100821
- if (typeof window === "undefined") return;
100822
- localStorage.removeItem(this.STORAGE_KEY);
100823
- } catch (error) {
100824
- console.error("Error clearing learning objectives from Local Storage:", error);
100825
- }
100826
- }
100827
- /**
100828
- * Parses TSV content into an array of LearningObjective objects.
100829
- * @param tsvContent The raw string content from a .tsv file.
100830
- * @returns An object containing the successfully parsed data and any errors encountered.
100831
- */
100832
- static parseTSV(tsvContent) {
100833
- const lines = tsvContent.split("\n").filter((line) => line.trim() !== "");
100834
- if (lines.length < 2) {
100835
- return { data: [], errors: ["File is empty or contains only a header."] };
100836
- }
100837
- const headerLine = lines.shift();
100838
- const headers = headerLine.split(" ").map((h3) => h3.trim());
100839
- if (headers.length !== this.EXPECTED_HEADERS.length || !this.EXPECTED_HEADERS.every((h3, i2) => h3 === headers[i2])) {
100840
- const errorMsg = `Invalid TSV header. Expected: "${this.EXPECTED_HEADERS.join(" ")}". Received: "${headers.join(" ")}"`;
100841
- return { data: [], errors: [errorMsg] };
100842
- }
100843
- const data = [];
100844
- const errors2 = [];
100845
- lines.forEach((line, index3) => {
100846
- const values = line.split(" ").map((v) => v.trim());
100847
- if (values.length !== this.EXPECTED_HEADERS.length) {
100848
- errors2.push(`Line ${index3 + 2}: Incorrect number of columns. Expected ${this.EXPECTED_HEADERS.length}, but got ${values.length}.`);
100849
- return;
100850
- }
100851
- const [
100852
- loId,
100853
- loDescription,
100854
- subject,
100855
- category,
100856
- topic,
100857
- keywordsStr,
100858
- grade,
100859
- stemElementsStr,
100860
- bloomLevelsStr
100861
- ] = values;
100862
- if (!loId || !subject || !category || !topic) {
100863
- errors2.push(`Line ${index3 + 2}: Missing required fields (LO ID, Subject, Category, or Topic).`);
100864
- return;
100865
- }
100866
- const learningObjective = {
100867
- loId,
100868
- loDescription,
100869
- subject,
100870
- category,
100871
- topic,
100872
- keywords: keywordsStr.split(",").map((k3) => k3.trim()).filter(Boolean),
100873
- grade,
100874
- stemElements: stemElementsStr.split(",").map((s4) => s4.trim()).filter(Boolean),
100875
- bloomLevelsGuideline: bloomLevelsStr.split(",").map((b2) => b2.trim()).filter(Boolean)
100876
- };
100877
- data.push(learningObjective);
100878
- });
100879
- return { data, errors: errors2 };
100880
- }
100881
- /**
100882
- * Gets a unique list of all subjects from the stored data.
100883
- * @returns An array of subject strings.
100884
- */
100885
- static getSubjects() {
100886
- const data = this.getData();
100887
- const subjects = data.map((item) => item.subject);
100888
- return [...new Set(subjects)].sort();
100889
- }
100890
- /**
100891
- * Gets a unique list of categories for a given subject.
100892
- * @param subject The subject to filter by.
100893
- * @returns An array of category strings.
100894
- */
100895
- static getCategoriesBySubject(subject) {
100896
- const data = this.getData();
100897
- const categories = data.filter((item) => item.subject === subject).map((item) => item.category);
100898
- return [...new Set(categories)].sort();
100899
- }
100900
- /**
100901
- * Gets a unique list of topics for a given category.
100902
- * @param category The category to filter by.
100903
- * @returns An array of topic strings.
100904
- */
100905
- static getTopicsByCategory(category) {
100906
- const data = this.getData();
100907
- const topics = data.filter((item) => item.category === category).map((item) => item.topic);
100908
- return [...new Set(topics)].sort();
100909
- }
100910
- /**
100911
- * Retrieves all LearningObjective details for a given list of topics.
100912
- * @param topics An array of topic strings to search for.
100913
- * @returns An array of matching LearningObjective objects.
100914
- */
100915
- static getLearningObjectivesByTopics(topics) {
100916
- const data = this.getData();
100917
- const topicSet = new Set(topics);
100918
- return data.filter((item) => topicSet.has(item.topic));
100919
- }
100920
- };
100921
- TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
100922
- TopicDataService.EXPECTED_HEADERS = [
100923
- "LO ID",
100924
- "LO Description",
100925
- "Subject",
100926
- "Category",
100927
- "Topic",
100928
- "Keywords",
100929
- "Grade",
100930
- "STEM Element(s)",
100931
- "Bloom\u2019s Level(s) Guideline"
100932
- ];
100933
-
100934
100134
  // src/ai/flows/question-gen/generate-mrq-question.ts
100935
100135
  init_react_shim();
100936
100136
 
@@ -100947,7 +100147,7 @@ var AIMRQOutputFieldsSchema = zod.z.object({
100947
100147
  correctTempOptionIds: zod.z.array(zod.z.string()).min(1),
100948
100148
  explanation: zod.z.string().optional(),
100949
100149
  points: zod.z.number().optional().default(10),
100950
- difficulty: zod.z.enum(["easy", "medium", "hard"]).optional(),
100150
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]).optional(),
100951
100151
  topic: zod.z.string().optional(),
100952
100152
  verifiedCategory: zod.z.string().optional().describe("The category this question actually addresses.")
100953
100153
  });
@@ -100966,7 +100166,7 @@ Previous attempts failed due to validation errors. Pay close attention to the nu
100966
100166
  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.` : "";
100967
100167
  const contextStrings = [
100968
100168
  `**Required Category:** ${category} (This is the ONLY language to be used)`,
100969
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
100169
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
100970
100170
  imageContextInstruction,
100971
100171
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
100972
100172
  quizContext?.targetMisconception && `**Target Misconception:** Use this to create plausible incorrect answers (distractors). The misconception is: "${quizContext.targetMisconception}"`,
@@ -100984,7 +100184,7 @@ Previous attempts failed due to validation errors. Pay close attention to the nu
100984
100184
  correctTempOptionIds: ["A", "C", "D"],
100985
100185
  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.",
100986
100186
  points: 10,
100987
- difficulty: "medium",
100187
+ difficulty: "Medium",
100988
100188
  topic: "Programming Paradigms",
100989
100189
  verifiedCategory: category
100990
100190
  }, null, 2);
@@ -101148,7 +100348,7 @@ var AIShortAnswerOutputFieldsSchema = zod.z.object({
101148
100348
  // isCaseSensitive không cần thiết ở đây, chúng ta sẽ quản lý nó ở phía client
101149
100349
  explanation: zod.z.string().optional(),
101150
100350
  points: zod.z.number().optional().default(10),
101151
- difficulty: zod.z.enum(["easy", "medium", "hard"]).optional(),
100351
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]).optional(),
101152
100352
  topic: zod.z.string().optional(),
101153
100353
  verifiedCategory: zod.z.string().optional()
101154
100354
  // Thêm để xác thực
@@ -101168,7 +100368,7 @@ Previous attempts failed. Ensure 'acceptedAnswers' is a non-empty array of strin
101168
100368
  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.` : "";
101169
100369
  const contextStrings = [
101170
100370
  `**Required Category:** ${category}`,
101171
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
100371
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
101172
100372
  imageContextInstruction,
101173
100373
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
101174
100374
  quizContext?.targetMisconception && `**Target Misconception:** The question should require an answer that corrects this specific misconception: "${quizContext.targetMisconception}"`
@@ -101178,7 +100378,7 @@ Previous attempts failed. Ensure 'acceptedAnswers' is a non-empty array of strin
101178
100378
  acceptedAnswers: ["let"],
101179
100379
  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.",
101180
100380
  points: 10,
101181
- difficulty: "easy",
100381
+ difficulty: "Easy",
101182
100382
  topic: "Swift Constants",
101183
100383
  verifiedCategory: category
101184
100384
  }, null, 2);
@@ -101311,7 +100511,7 @@ var AINumericOutputFieldsSchema = zod.z.object({
101311
100511
  tolerance: zod.z.number().min(0).optional(),
101312
100512
  explanation: zod.z.string().optional(),
101313
100513
  points: zod.z.number().optional().default(10),
101314
- difficulty: zod.z.enum(["easy", "medium", "hard"]).optional(),
100514
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]).optional(),
101315
100515
  topic: zod.z.string().optional(),
101316
100516
  verifiedCategory: zod.z.string().optional()
101317
100517
  // Thêm để xác thực
@@ -101331,7 +100531,7 @@ Previous attempts failed. Ensure the 'answer' is a valid number and fits within
101331
100531
  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.` : "";
101332
100532
  const contextStrings = [
101333
100533
  `**Required Category:** ${category}`,
101334
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
100534
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
101335
100535
  imageContextInstruction,
101336
100536
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
101337
100537
  quizContext?.targetMisconception && `**Target Misconception:** The question should clarify this numerical error: "${quizContext.targetMisconception}"`
@@ -101347,7 +100547,7 @@ Previous attempts failed. Ensure the 'answer' is a valid number and fits within
101347
100547
  tolerance: 0,
101348
100548
  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).",
101349
100549
  points: 10,
101350
- difficulty: "medium",
100550
+ difficulty: "Medium",
101351
100551
  topic: "Swift Data Types",
101352
100552
  verifiedCategory: category
101353
100553
  }, null, 2);
@@ -101497,7 +100697,7 @@ var AIFillInTheBlanksOutputFieldsSchema = zod.z.object({
101497
100697
  })).min(1).describe("An array of text and blank segments representing the question."),
101498
100698
  explanation: zod.z.string().optional(),
101499
100699
  points: zod.z.number().optional().default(10),
101500
- difficulty: zod.z.enum(["easy", "medium", "hard"]).optional(),
100700
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]).optional(),
101501
100701
  topic: zod.z.string().optional(),
101502
100702
  verifiedCategory: zod.z.string().optional()
101503
100703
  // Thêm để xác thực
@@ -101517,7 +100717,7 @@ Previous attempts failed. Pay strict attention to the JSON schema, especially th
101517
100717
  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.` : "";
101518
100718
  const contextStrings = [
101519
100719
  `**Required Category:** ${category}`,
101520
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
100720
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
101521
100721
  imageContextInstruction,
101522
100722
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
101523
100723
  quizContext?.targetMisconception && `**Target Misconception:** Design the blank to test this specific point: "${quizContext.targetMisconception}"`
@@ -101531,7 +100731,7 @@ Previous attempts failed. Pay strict attention to the JSON schema, especially th
101531
100731
  ],
101532
100732
  explanation: "The 'func' keyword is used to declare a function in the Swift programming language.",
101533
100733
  points: 10,
101534
- difficulty: "easy",
100734
+ difficulty: "Easy",
101535
100735
  topic: "Swift Function Declaration",
101536
100736
  verifiedCategory: category
101537
100737
  }, null, 2);
@@ -101685,7 +100885,7 @@ var AISequenceOutputFieldsSchema = zod.z.object({
101685
100885
  itemsInCorrectOrder: zod.z.array(zod.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."),
101686
100886
  explanation: zod.z.string().optional(),
101687
100887
  points: zod.z.number().optional().default(10),
101688
- difficulty: zod.z.enum(["easy", "medium", "hard"]).optional(),
100888
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]).optional(),
101689
100889
  topic: zod.z.string().optional(),
101690
100890
  verifiedCategory: zod.z.string().optional()
101691
100891
  // Thêm để xác thực
@@ -101705,7 +100905,7 @@ Previous attempts failed. Ensure the 'itemsInCorrectOrder' array has exactly the
101705
100905
  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.` : "";
101706
100906
  const contextStrings = [
101707
100907
  `**Required Category:** ${category}`,
101708
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
100908
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
101709
100909
  imageContextInstruction,
101710
100910
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
101711
100911
  quizContext?.targetMisconception && `**Target Misconception:** The sequence should clarify this specific process error: "${quizContext.targetMisconception}"`
@@ -101720,7 +100920,7 @@ Previous attempts failed. Ensure the 'itemsInCorrectOrder' array has exactly the
101720
100920
  ],
101721
100921
  explanation: "This is the fundamental sequence for a basic data task in URLSession.",
101722
100922
  points: 10,
101723
- difficulty: "medium",
100923
+ difficulty: "Medium",
101724
100924
  topic: "Swift Networking",
101725
100925
  verifiedCategory: category
101726
100926
  }, null, 2);
@@ -101862,7 +101062,7 @@ var AIMatchingOutputFieldsSchema = zod.z.object({
101862
101062
  })).min(2),
101863
101063
  explanation: zod.z.string().optional(),
101864
101064
  points: zod.z.number().optional().default(10),
101865
- difficulty: zod.z.enum(["easy", "medium", "hard"]).optional(),
101065
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]).optional(),
101866
101066
  topic: zod.z.string().optional(),
101867
101067
  verifiedCategory: zod.z.string().optional()
101868
101068
  // Thêm để xác thực
@@ -101882,7 +101082,7 @@ Previous attempts failed. Please ensure the 'correctPairs' array has exactly the
101882
101082
  const imageContextInstruction = imageUrl ? `**Image Context:** You MUST analyze the provided image. The matching pairs must be directly related to the content of this image.` : "";
101883
101083
  const contextStrings = [
101884
101084
  `**Required Category:** ${category}`,
101885
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
101085
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
101886
101086
  imageContextInstruction,
101887
101087
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
101888
101088
  quizContext?.targetMisconception && `**Target Misconception:** Design a pair that specifically tests this confusion: "${quizContext.targetMisconception}"`
@@ -101896,7 +101096,7 @@ Previous attempts failed. Please ensure the 'correctPairs' array has exactly the
101896
101096
  ],
101897
101097
  explanation: "These are the fundamental characteristics of Swift's main collection types.",
101898
101098
  points: 10,
101899
- difficulty: "easy",
101099
+ difficulty: "Easy",
101900
101100
  topic: "Swift Collection Types",
101901
101101
  verifiedCategory: category
101902
101102
  }, null, 2);
@@ -102026,6 +101226,821 @@ async function generateMatchingQuestion(clientInput, apiKey) {
102026
101226
  return { error: errorMessage };
102027
101227
  }
102028
101228
 
101229
+ // src/react-ui/components/authoring/AIQuestionGeneratorModal.tsx
101230
+ var supportedQuestionTypesForAI = [
101231
+ { value: "true_false", label: "True/False" },
101232
+ { value: "multiple_choice", label: "Multiple Choice" },
101233
+ { value: "multiple_response", label: "Multiple Response" },
101234
+ { value: "short_answer", label: "Short Answer" },
101235
+ { value: "numeric", label: "Numeric" },
101236
+ { value: "fill_in_the_blanks", label: "Fill In The Blanks" },
101237
+ { value: "sequence", label: "Sequence" },
101238
+ { value: "matching", label: "Matching" }
101239
+ ];
101240
+ var AIQuestionGeneratorModal = ({
101241
+ isOpen,
101242
+ onClose,
101243
+ onQuestionGenerated,
101244
+ language: language3,
101245
+ questionType: questionTypeProp,
101246
+ subjects = [],
101247
+ topics = [],
101248
+ gradeLevels = [],
101249
+ bloomLevels = []
101250
+ }) => {
101251
+ const [prompt, setPrompt] = React169.useState("");
101252
+ const [isLoading, setIsLoading] = React169.useState(false);
101253
+ const [error, setError] = React169.useState(null);
101254
+ const { toast: toast2 } = useToast();
101255
+ const [subjectCode, setSubjectCode] = React169.useState("");
101256
+ const [topicCode, setTopicCode] = React169.useState("");
101257
+ const [gradeBand, setGradeBand] = React169.useState("");
101258
+ const [bloomLevelCode, setBloomLevelCode] = React169.useState("");
101259
+ const [selectedQuestionType, setSelectedQuestionType] = React169.useState("multiple_choice");
101260
+ const [numberOfOptions, setNumberOfOptions] = React169.useState(4);
101261
+ const [minCorrectAnswers, setMinCorrectAnswers] = React169.useState(2);
101262
+ const [maxCorrectAnswers, setMaxCorrectAnswers] = React169.useState(3);
101263
+ const [isCaseSensitive, setIsCaseSensitive] = React169.useState(false);
101264
+ const [numberOfBlanks, setNumberOfBlanks] = React169.useState(2);
101265
+ const [numberOfSequenceItems, setNumberOfSequenceItems] = React169.useState(4);
101266
+ const [numberOfMatchingPairs, setNumberOfMatchingPairs] = React169.useState(4);
101267
+ const [isApiKeyManagerModalOpen, setIsApiKeyManagerModalOpen] = React169.useState(false);
101268
+ const [geminiApiKeyExists, setGeminiApiKeyExists] = React169.useState(false);
101269
+ const finalQuestionType = questionTypeProp || selectedQuestionType;
101270
+ React169.useEffect(() => {
101271
+ if (isOpen) {
101272
+ setPrompt("");
101273
+ setError(null);
101274
+ setIsLoading(false);
101275
+ setSubjectCode("");
101276
+ setTopicCode("");
101277
+ setGradeBand("");
101278
+ setBloomLevelCode("");
101279
+ setSelectedQuestionType(questionTypeProp || "multiple_choice");
101280
+ setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
101281
+ }
101282
+ }, [isOpen, questionTypeProp]);
101283
+ const filteredTopics = React169.useMemo(() => {
101284
+ if (!subjectCode) return [];
101285
+ return topics.filter((t4) => t4.subjectCode === subjectCode);
101286
+ }, [subjectCode, topics]);
101287
+ const handleApiKeyModalClose = () => {
101288
+ setIsApiKeyManagerModalOpen(false);
101289
+ setGeminiApiKeyExists(APIKeyService.hasAPIKey(GEMINI_API_KEY_SERVICE_NAME));
101290
+ };
101291
+ const handleSubmit = async () => {
101292
+ if (!prompt.trim()) {
101293
+ setError("Please provide a prompt for the question.");
101294
+ return;
101295
+ }
101296
+ const geminiKey = APIKeyService.getAPIKey(GEMINI_API_KEY_SERVICE_NAME);
101297
+ if (!geminiKey) {
101298
+ setError("Gemini API Key is not set. Please configure it.");
101299
+ setGeminiApiKeyExists(false);
101300
+ return;
101301
+ }
101302
+ setGeminiApiKeyExists(true);
101303
+ setError(null);
101304
+ setIsLoading(true);
101305
+ try {
101306
+ const quizContext = {
101307
+ plannedTopic: prompt,
101308
+ originalSubject: subjectCode,
101309
+ originalTopic: topicCode,
101310
+ gradeBand,
101311
+ plannedBloomLevel: bloomLevelCode
101312
+ };
101313
+ const baseClientInput = {
101314
+ language: language3,
101315
+ difficulty: "Medium",
101316
+ quizContext
101317
+ };
101318
+ let generatedResult = {};
101319
+ switch (finalQuestionType) {
101320
+ case "true_false":
101321
+ generatedResult = await generateTrueFalseQuestion(baseClientInput, geminiKey);
101322
+ break;
101323
+ case "multiple_choice":
101324
+ generatedResult = await generateMCQQuestion({ ...baseClientInput, numberOfOptions }, geminiKey);
101325
+ break;
101326
+ case "multiple_response":
101327
+ generatedResult = await generateMRQQuestion({ ...baseClientInput, numberOfOptions, minCorrectAnswers, maxCorrectAnswers }, geminiKey);
101328
+ break;
101329
+ case "short_answer":
101330
+ generatedResult = await generateShortAnswerQuestion({ ...baseClientInput, isCaseSensitive }, geminiKey);
101331
+ break;
101332
+ case "numeric":
101333
+ generatedResult = await generateNumericQuestion({ ...baseClientInput, allowDecimals: true, tolerance: 0 }, geminiKey);
101334
+ break;
101335
+ case "fill_in_the_blanks":
101336
+ generatedResult = await generateFillInTheBlanksQuestion({ ...baseClientInput, numberOfBlanks, isCaseSensitive }, geminiKey);
101337
+ break;
101338
+ case "sequence":
101339
+ generatedResult = await generateSequenceQuestion({ ...baseClientInput, numberOfItems: numberOfSequenceItems }, geminiKey);
101340
+ break;
101341
+ case "matching":
101342
+ generatedResult = await generateMatchingQuestion({ ...baseClientInput, numberOfPairs: numberOfMatchingPairs, shuffleOptions: true }, geminiKey);
101343
+ break;
101344
+ default:
101345
+ throw new Error(`AI generation for '${finalQuestionType}' is not implemented in this flow.`);
101346
+ }
101347
+ if (generatedResult.error) {
101348
+ throw new Error(generatedResult.error);
101349
+ }
101350
+ if (generatedResult.question) {
101351
+ const completeQuestion = generatedResult.question;
101352
+ completeQuestion.subject = subjectCode;
101353
+ completeQuestion.topic = topicCode;
101354
+ completeQuestion.gradeBand = gradeBand;
101355
+ completeQuestion.bloomLevel = bloomLevelCode;
101356
+ if (completeQuestion.points === void 0) completeQuestion.points = 10;
101357
+ onQuestionGenerated(completeQuestion);
101358
+ toast2({ title: "AI Question Generated", description: "Review and edit the generated question." });
101359
+ onClose();
101360
+ } else {
101361
+ throw new Error("AI did not return a valid question object.");
101362
+ }
101363
+ } catch (e3) {
101364
+ console.error("AI Question Generation Error:", e3);
101365
+ let errorMessage = e3.message || "An unknown error occurred.";
101366
+ if (typeof errorMessage === "string" && errorMessage.includes("API key not valid")) {
101367
+ errorMessage = "The provided Google Gemini API Key is invalid. Please check it.";
101368
+ setGeminiApiKeyExists(false);
101369
+ }
101370
+ setError(errorMessage);
101371
+ toast2({ title: "AI Generation Failed", description: errorMessage, variant: "destructive" });
101372
+ } finally {
101373
+ setIsLoading(false);
101374
+ }
101375
+ };
101376
+ const comboboxOptions = {
101377
+ subjects: subjects.map((s4) => ({ value: s4.code, label: s4.name })),
101378
+ topics: filteredTopics.map((t4) => ({ value: t4.code, label: t4.name })),
101379
+ gradeLevels: gradeLevels.map((g) => ({ value: g.code, label: g.name })),
101380
+ bloomLevels: bloomLevels.map((b2) => ({ value: b2.code, label: b2.name }))
101381
+ };
101382
+ const renderSpecificParams = () => {
101383
+ switch (finalQuestionType) {
101384
+ case "multiple_choice":
101385
+ case "multiple_response":
101386
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-num-options" }, "Number of Options (2-8)"), /* @__PURE__ */ React169__namespace.default.createElement(
101387
+ Input,
101388
+ {
101389
+ id: "ai-num-options",
101390
+ type: "number",
101391
+ value: numberOfOptions,
101392
+ onChange: (e3) => setNumberOfOptions(parseInt(e3.target.value, 10)),
101393
+ min: 2,
101394
+ max: 8
101395
+ }
101396
+ ), finalQuestionType === "multiple_response" && /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-min-correct" }, "Min Correct Answers"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-min-correct", type: "number", value: minCorrectAnswers, onChange: (e3) => setMinCorrectAnswers(parseInt(e3.target.value, 10)), min: 1, max: numberOfOptions }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-max-correct" }, "Max Correct Answers"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-max-correct", type: "number", value: maxCorrectAnswers, onChange: (e3) => setMaxCorrectAnswers(parseInt(e3.target.value, 10)), min: minCorrectAnswers, max: numberOfOptions })));
101397
+ case "short_answer":
101398
+ case "fill_in_the_blanks":
101399
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-2 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement("input", { type: "checkbox", id: "ai-case-sensitive", checked: isCaseSensitive, onChange: (e3) => setIsCaseSensitive(e3.target.checked), className: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary" }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-case-sensitive" }, "Case Sensitive Answers"), finalQuestionType === "fill_in_the_blanks" && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1 ml-4" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-num-blanks" }, "Number of Blanks (1-5)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-num-blanks", type: "number", value: numberOfBlanks, onChange: (e3) => setNumberOfBlanks(parseInt(e3.target.value, 10)), min: 1, max: 5 })));
101400
+ case "sequence":
101401
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-num-seq-items" }, "Number of Items to Sequence (2-10)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-num-seq-items", type: "number", value: numberOfSequenceItems, onChange: (e3) => setNumberOfSequenceItems(parseInt(e3.target.value, 10)), min: 2, max: 10 }));
101402
+ case "matching":
101403
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-num-match-pairs" }, "Number of Pairs to Match (2-8)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "ai-num-match-pairs", type: "number", value: numberOfMatchingPairs, onChange: (e3) => setNumberOfMatchingPairs(parseInt(e3.target.value, 10)), min: 2, max: 8 }));
101404
+ default:
101405
+ return null;
101406
+ }
101407
+ };
101408
+ return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => {
101409
+ if (!open) onClose();
101410
+ } }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-[600px]" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, { className: "flex items-center font-headline text-2xl" }, /* @__PURE__ */ React169__namespace.default.createElement(WandSparkles, { className: "mr-2 h-6 w-6 text-primary" }), " AI Question Generator"), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, "Provide a prompt and metadata to generate a '", finalQuestionType, "' question.")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 py-4 max-h-[60vh] overflow-y-auto px-2" }, !geminiApiKeyExists && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-3 mb-4 border border-amber-500 bg-amber-50 rounded-md text-amber-700" }), !questionTypeProp && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-q-type-select" }, "Question Type"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedQuestionType, onValueChange: (v) => setSelectedQuestionType(v) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "ai-q-type-select" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, supportedQuestionTypesForAI.map((type) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: type.value, value: type.value }, type.label))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "ai-prompt" }, "Prompt / Topic"), /* @__PURE__ */ React169__namespace.default.createElement(
101411
+ Textarea,
101412
+ {
101413
+ id: "ai-prompt",
101414
+ value: prompt,
101415
+ onChange: (e3) => setPrompt(e3.target.value),
101416
+ placeholder: "e.g., The process of photosynthesis, The causes of World War II, Basic Algebra Equations",
101417
+ className: "min-h-[100px]"
101418
+ }
101419
+ )), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.subjects, value: subjectCode, onChange: setSubjectCode, placeholder: "Select or type a Subject..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Topic"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.topics, value: topicCode, onChange: setTopicCode, placeholder: "Select or type a Topic...", disabled: !subjectCode })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Grade Level"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.gradeLevels, value: gradeBand, onChange: setGradeBand, placeholder: "Select or type a Grade..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Bloom's Level"), /* @__PURE__ */ React169__namespace.default.createElement(EditableCombobox, { options: comboboxOptions.bloomLevels, value: bloomLevelCode, onChange: setBloomLevelCode, placeholder: "Select a Bloom's Level..." }))), renderSpecificParams(), error && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-destructive flex items-center mt-2" }, /* @__PURE__ */ React169__namespace.default.createElement(TriangleAlert, { className: "mr-1 h-4 w-4" }), " ", error)), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline" }, "Cancel")), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", onClick: handleSubmit, disabled: isLoading || !prompt.trim() || !geminiApiKeyExists }, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__namespace.default.createElement(WandSparkles, { className: "mr-2 h-4 w-4" }), "Generate Question")))), /* @__PURE__ */ React169__namespace.default.createElement(APIKeyManagerModal, { isOpen: isApiKeyManagerModalOpen, onClose: handleApiKeyModalClose }));
101420
+ };
101421
+
101422
+ // src/react-ui/components/authoring/AIFullQuizGeneratorModal.tsx
101423
+ init_react_shim();
101424
+
101425
+ // src/ai/flows/generate-quiz-plan.ts
101426
+ init_react_shim();
101427
+
101428
+ // src/ai/flows/generate-quiz-plan-types.ts
101429
+ init_react_shim();
101430
+ var TopicWithMetadataSchema = zod.z.object({
101431
+ topic: zod.z.string().min(1),
101432
+ ratio: zod.z.number().min(0).max(100),
101433
+ originalLoId: zod.z.string().optional(),
101434
+ originalSubject: zod.z.string().optional(),
101435
+ originalCategory: zod.z.string().optional(),
101436
+ originalTopic: zod.z.string().optional(),
101437
+ commonMisconceptions: zod.z.array(zod.z.string()).optional()
101438
+ });
101439
+ var BloomLevelStringsEnum = zod.z.enum(["remembering", "understanding", "applying", "analyzing", "evaluating", "creating"]);
101440
+ var fullQuizSupportedQuestionTypesArray = [
101441
+ "true_false",
101442
+ "multiple_choice",
101443
+ "multiple_response",
101444
+ "short_answer",
101445
+ "numeric",
101446
+ "fill_in_the_blanks",
101447
+ "sequence",
101448
+ "matching",
101449
+ "drag_and_drop",
101450
+ "coding"
101451
+ ];
101452
+ zod.z.object({
101453
+ language: zod.z.string().optional().default("English"),
101454
+ totalQuestions: zod.z.number().int().min(1).max(50),
101455
+ numCodingQuestions: zod.z.number().optional().default(0),
101456
+ topics: zod.z.array(TopicWithMetadataSchema).min(1),
101457
+ bloomLevels: zod.z.array(zod.z.object({
101458
+ level: BloomLevelStringsEnum,
101459
+ ratio: zod.z.number().min(0).max(100)
101460
+ })).min(1),
101461
+ selectedContextIds: zod.z.array(zod.z.string()).optional(),
101462
+ selectedQuestionTypes: zod.z.array(zod.z.enum(fullQuizSupportedQuestionTypesArray)).min(1),
101463
+ imageContexts: zod.z.array(zod.z.custom()).optional().describe("Library of available image contexts for the AI to use.")
101464
+ });
101465
+ var PlannedQuestionSchema = zod.z.object({
101466
+ plannedTopic: zod.z.string().min(1).describe("The specific, assessable topic for this question."),
101467
+ plannedQuestionType: zod.z.enum(fullQuizSupportedQuestionTypesArray).describe("The specific question type chosen."),
101468
+ plannedBloomLevel: BloomLevelStringsEnum.describe("The Bloom's level assigned."),
101469
+ plannedContextId: zod.z.string().optional().describe("The specific context ID chosen for this question."),
101470
+ imageId: zod.z.string().nullable().optional().describe("The ID of the image from the context library to be used for this question."),
101471
+ targetMisconception: zod.z.string().optional().describe("A specific common misconception this question should target."),
101472
+ difficultyReason: zod.z.string().optional().describe("Strategic explanation of difficulty choice and placement."),
101473
+ topicSpecificity: zod.z.enum(["broad", "focused", "specific"]).optional().describe("How specific the topic coverage should be."),
101474
+ originalLoId: zod.z.string().optional(),
101475
+ originalSubject: zod.z.string().optional(),
101476
+ originalCategory: zod.z.string().optional(),
101477
+ originalTopic: zod.z.string().optional()
101478
+ });
101479
+ var GenerateQuizPlanOutputSchema = zod.z.object({
101480
+ quizPlan: zod.z.array(PlannedQuestionSchema).describe("A detailed plan for each question in the quiz."),
101481
+ diversityMetrics: zod.z.object({
101482
+ questionTypeDistribution: zod.z.record(zod.z.number()).optional(),
101483
+ bloomLevelDistribution: zod.z.record(zod.z.number()).optional(),
101484
+ maxConsecutiveSameType: zod.z.number().optional()
101485
+ }).optional().describe("Metrics showing the diversity achieved in the plan."),
101486
+ planningStrategy: zod.z.object({
101487
+ overallApproach: zod.z.string().optional(),
101488
+ keyDecisions: zod.z.array(zod.z.string()).optional()
101489
+ }).optional()
101490
+ });
101491
+
101492
+ // src/ai/flows/generate-quiz-plan.ts
101493
+ var QuizPlanLogger = class {
101494
+ constructor() {
101495
+ this.logs = [];
101496
+ this.startTime = Date.now();
101497
+ }
101498
+ log(phase, data, duration) {
101499
+ this.logs.push({
101500
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
101501
+ phase,
101502
+ data,
101503
+ duration
101504
+ });
101505
+ if (duration !== void 0) {
101506
+ console.log(`[${phase}] Completed in ${duration}ms:`, data);
101507
+ } else {
101508
+ console.log(`[${phase}]:`, data);
101509
+ }
101510
+ }
101511
+ getLogs() {
101512
+ return this.logs;
101513
+ }
101514
+ getTotalDuration() {
101515
+ return Date.now() - this.startTime;
101516
+ }
101517
+ };
101518
+ function generateQuestionTypeSelectionGuidance() {
101519
+ return `
101520
+ QUESTION TYPE SELECTION BEST PRACTICES:
101521
+
101522
+ **TRUE_FALSE (true_false)**
101523
+ - Best for: Binary concepts, fact verification, common misconceptions
101524
+ - Bloom levels: Primarily Remembering, Understanding
101525
+ - Use when: Testing definitive statements, clarifying misconceptions
101526
+ - Example: "Photosynthesis only occurs during daytime" (targets timing misconception)
101527
+
101528
+ **MULTIPLE CHOICE (multiple_choice)**
101529
+ - Best for: Concept selection, process understanding, comparison
101530
+ - Bloom levels: All levels, especially Understanding and Applying
101531
+ - Use when: Testing conceptual understanding with clear alternatives
101532
+ - Example: "Which factor most affects enzyme activity?" (applying knowledge)
101533
+
101534
+ **MULTIPLE RESPONSE (multiple_response)**
101535
+ - Best for: Identifying multiple correct factors, comprehensive understanding
101536
+ - Bloom levels: Understanding, Analyzing, Evaluating
101537
+ - Use when: Multiple correct answers exist, testing thorough knowledge
101538
+ - Example: "Select all factors that influence plant growth" (analyzing components)
101539
+
101540
+ **FILL IN THE BLANKS (fill_in_the_blanks)**
101541
+ - Best for: Key terminology, formulas, specific facts
101542
+ - Bloom levels: Remembering, Understanding
101543
+ - Use when: Testing precise recall of important terms/concepts
101544
+ - Example: "The process of _____ converts light energy to chemical energy"
101545
+
101546
+ **NUMERIC (numeric)**
101547
+ - Best for: Calculations, quantitative problems, formula application
101548
+ - Bloom levels: Applying, Analyzing
101549
+ - Use when: Mathematical computations are required
101550
+ - Example: "Calculate the molarity of a 2L solution containing 0.5 moles of NaCl"
101551
+
101552
+ **MATCHING (matching)**
101553
+ - Best for: Connecting related concepts, terminology pairs
101554
+ - Bloom levels: Remembering, Understanding
101555
+ - Use when: Testing relationships between terms and definitions
101556
+ - Example: Match organelles with their functions
101557
+
101558
+ **SEQUENCE (sequence)**
101559
+ - Best for: Process steps, chronological order, procedural knowledge
101560
+ - Bloom levels: Understanding, Applying, Analyzing
101561
+ - Use when: Order or sequence is critical to understanding
101562
+ - Example: "Arrange the steps of mitosis in correct order"
101563
+
101564
+ **DRAG AND DROP (drag_and_drop)**
101565
+ - Best for: Categorization, classification, spatial relationships
101566
+ - Bloom levels: Understanding, Applying, Analyzing
101567
+ - Use when: Grouping or organizing information is key
101568
+ - Example: "Classify these compounds as acids, bases, or neutral"
101569
+
101570
+ **SHORT ANSWER (short_answer)**
101571
+ - Best for: Explanations, definitions, problem-solving steps
101572
+ - Bloom levels: Understanding, Applying, Analyzing, Evaluating, Creating
101573
+ - Use when: Requiring explanatory responses, open-ended thinking
101574
+ - Example: "Explain why enzymes are specific to certain substrates"
101575
+
101576
+ **CODING (coding)**
101577
+ - Best for: Programming problems, algorithm implementation
101578
+ - Bloom levels: Applying, Analyzing, Evaluating, Creating
101579
+ - Use when: Technical implementation or code analysis is required
101580
+ - Example: "Write a function to calculate factorial recursively"
101581
+ `;
101582
+ }
101583
+ function generateAdvancedBloomGuidance() {
101584
+ return `
101585
+ ADVANCED BLOOM'S TAXONOMY & QUESTION TYPE OPTIMIZATION:
101586
+
101587
+ **REMEMBERING (Knowledge Recall)**
101588
+ - Primary types: true_false, fill_in_the_blanks, matching
101589
+ - Secondary types: multiple_choice (simple recall)
101590
+ - Focus: Facts, terms, basic concepts, definitions
101591
+ - Misconception addressing: Use true_false to clarify common confusions
101592
+
101593
+ **UNDERSTANDING (Comprehension)**
101594
+ - Primary types: multiple_choice, short_answer, matching
101595
+ - Secondary types: multiple_response, sequence
101596
+ - Focus: Explanations, interpretations, examples, classifications
101597
+ - Best for: "Explain why...", "What does this mean...", "Give an example..."
101598
+
101599
+ **APPLYING (Using Knowledge)**
101600
+ - Primary types: numeric, short_answer, sequence, coding
101601
+ - Secondary types: multiple_choice, drag_and_drop
101602
+ - Focus: Problem-solving, implementing procedures, using methods
101603
+ - Best for: Calculations, step-by-step processes, practical applications
101604
+
101605
+ **ANALYZING (Breaking Down Information)**
101606
+ - Primary types: multiple_response, short_answer, sequence, coding
101607
+ - Secondary types: drag_and_drop, multiple_choice
101608
+ - Focus: Identifying components, relationships, cause-effect
101609
+ - Best for: "What are the factors...", "How do these relate...", "Break down..."
101610
+
101611
+ **EVALUATING (Making Judgments)**
101612
+ - Primary types: short_answer, multiple_response, coding
101613
+ - Secondary types: multiple_choice (with justification)
101614
+ - Focus: Critiquing, judging, comparing alternatives, decision-making
101615
+ - Best for: "Which is better and why...", "Evaluate the approach...", "Critique..."
101616
+
101617
+ **CREATING (Producing New Work)**
101618
+ - Primary types: short_answer, coding, sequence
101619
+ - Secondary types: drag_and_drop (design tasks)
101620
+ - Focus: Designing, planning, producing, constructing
101621
+ - Best for: "Design a solution...", "Create a plan...", "Develop a strategy..."
101622
+ `;
101623
+ }
101624
+ function generateDiversityRules() {
101625
+ return `
101626
+ ENHANCED DIVERSITY & QUALITY ASSURANCE RULES:
101627
+
101628
+ **Question Type Distribution Strategy:**
101629
+ 1. Never place more than 3 consecutive questions of the same type
101630
+ 2. Distribute question types based on their cognitive complexity
101631
+ 3. For quizzes with 10+ questions, use at least 4 different question types
101632
+ 4. For quizzes with 20+ questions, use at least 6 different question types
101633
+ 5. Balance quick-answer types (true_false, multiple_choice) with deeper types (short_answer, coding)
101634
+
101635
+ **Intelligent Difficulty Progression:**
101636
+ 1. Opening (20%): Start with confidence-building questions (Remembering/Understanding)
101637
+ 2. Building (40%): Gradually increase complexity (Understanding/Applying)
101638
+ 3. Peak (30%): Most challenging questions (Analyzing/Evaluating/Creating)
101639
+ 4. Closing (10%): Moderate challenge to end positively
101640
+
101641
+ **Misconception-Driven Planning:**
101642
+ - When common misconceptions are provided, prioritize question types that can effectively address them
101643
+ - Use true_false for binary misconceptions
101644
+ - Use multiple_choice for concept selection misconceptions
101645
+ - Use short_answer for complex misconceptions requiring explanation
101646
+
101647
+ **Contextual Intelligence:**
101648
+ - Programming/Technical topics: Favor coding, numeric, short_answer
101649
+ - Process-oriented topics: Favor sequence, short_answer, drag_and_drop
101650
+ - Conceptual topics: Favor multiple_choice, multiple_response, true_false
101651
+ - Factual topics: Favor fill_in_the_blanks, matching, true_false
101652
+ `;
101653
+ }
101654
+ async function generateQuizPlan(clientInput, apiKey, imageContexts = []) {
101655
+ const logger = new QuizPlanLogger();
101656
+ try {
101657
+ logger.log("VALIDATION_START", {
101658
+ totalQuestions: clientInput.totalQuestions,
101659
+ availableTypes: clientInput.selectedQuestionTypes,
101660
+ topicCount: clientInput.topics.length,
101661
+ bloomLevelCount: clientInput.bloomLevels.length
101662
+ });
101663
+ const totalTopicRatio = clientInput.topics.reduce((sum, t4) => sum + t4.ratio, 0);
101664
+ if (Math.abs(totalTopicRatio - 100) > 1) {
101665
+ throw new Error(`Total topic ratio must be 100%. Current sum: ${totalTopicRatio.toFixed(1)}%`);
101666
+ }
101667
+ const totalBloomRatio = clientInput.bloomLevels.reduce((sum, b2) => sum + b2.ratio, 0);
101668
+ if (Math.abs(totalBloomRatio - 100) > 1) {
101669
+ throw new Error(`Total Bloom level ratio must be 100%. Current sum: ${totalBloomRatio.toFixed(1)}%`);
101670
+ }
101671
+ logger.log("VALIDATION_SUCCESS", {
101672
+ topicRatioSum: totalTopicRatio,
101673
+ bloomRatioSum: totalBloomRatio
101674
+ });
101675
+ const aiStartTime = Date.now();
101676
+ const ai = new genai.GoogleGenAI({
101677
+ apiKey
101678
+ });
101679
+ const model = "gemini-2.5-pro";
101680
+ const config3 = {
101681
+ temperature: 0.8,
101682
+ thinkingConfig: {
101683
+ thinkingBudget: 4096
101684
+ },
101685
+ responseMimeType: "application/json"
101686
+ };
101687
+ logger.log("AI_INITIALIZATION", { model }, Date.now() - aiStartTime);
101688
+ const { language: language3, totalQuestions, numCodingQuestions = 0 } = clientInput;
101689
+ const promptStartTime = Date.now();
101690
+ const topicsDistribution = clientInput.topics.map((t4) => {
101691
+ 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}%`;
101692
+ if (t4.commonMisconceptions && t4.commonMisconceptions.length > 0) {
101693
+ topicString += `
101694
+ - Common Misconceptions: [${t4.commonMisconceptions.join(", ")}]`;
101695
+ }
101696
+ return topicString;
101697
+ }).join("\n ");
101698
+ const bloomDistribution = clientInput.bloomLevels.map(
101699
+ (b2) => `- Level: "${b2.level}", Ratio: ${b2.ratio}%`
101700
+ ).join("\n ");
101701
+ let questionTypesForPrompt = [...clientInput.selectedQuestionTypes];
101702
+ if (numCodingQuestions === 0) {
101703
+ questionTypesForPrompt = questionTypesForPrompt.filter(
101704
+ (type) => type !== "short_answer" && type !== "coding"
101705
+ );
101706
+ }
101707
+ const allowedQuestionTypes = questionTypesForPrompt.map((t4) => `'${t4}'`).join(", ");
101708
+ const codingRequirement = numCodingQuestions > 0 ? `
101709
+ **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.` : "";
101710
+ const imageContextSection = imageContexts && imageContexts.length > 0 ? `
101711
+ ## AVAILABLE IMAGE CONTEXT LIBRARY
101712
+ 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.
101713
+
101714
+ \`\`\`json
101715
+ ${JSON.stringify(imageContexts.map((img) => ({ imageId: img.id, subject: img.subject, category: img.category, topic: img.topic, description: img.detailedDescription })), null, 2)}
101716
+ \`\`\`
101717
+ ` : "";
101718
+ 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.
101719
+
101720
+ ${generateQuestionTypeSelectionGuidance()}
101721
+
101722
+ ${generateAdvancedBloomGuidance()}
101723
+
101724
+ ${generateDiversityRules()}
101725
+
101726
+ ## COMPREHENSIVE QUIZ REQUIREMENTS:
101727
+
101728
+ **Target Language**: ${language3}
101729
+ **Total Questions**: ${totalQuestions}${codingRequirement}
101730
+
101731
+ **Topic Distribution & Learning Context** (follow precisely):
101732
+ ${topicsDistribution}
101733
+
101734
+ **Cognitive Complexity Distribution** (follow precisely):
101735
+ ${bloomDistribution}
101736
+
101737
+ **Available Question Arsenal**: ${allowedQuestionTypes}
101738
+
101739
+ **Image Resources**: ${imageContextSection}
101740
+
101741
+ ## STRATEGIC PLANNING METHODOLOGY:
101742
+
101743
+ 1. **Misconception Analysis**: If common misconceptions are provided, design questions specifically to address and correct them
101744
+ 2. **Question Type Intelligence**: Select question types based on the cognitive demands and content nature
101745
+ 3. **Difficulty Orchestration**: Create a learning journey that builds confidence while challenging appropriately
101746
+ 4. **Diversity Optimization**: Ensure variety prevents monotony and maintains engagement
101747
+ 5. **Context Sensitivity**: Match question types to topic characteristics (technical, conceptual, procedural)
101748
+ 6. **Image Context Integration**: Intelligently associate questions with relevant images from the provided library to enhance contextual understanding.
101749
+
101750
+ ## ENHANCED OUTPUT FORMAT:
101751
+
101752
+ Return ONLY a JSON object with this EXACT structure:
101753
+
101754
+ \`\`\`json
101755
+ {
101756
+ "quizPlan": [
101757
+ {
101758
+ "plannedTopic": "Specific, assessable topic derived from provided context",
101759
+ "plannedQuestionType": "question_type_from_allowed_list",
101760
+ "plannedBloomLevel": "bloom_level_from_requirements",
101761
+ "plannedContextId": "THEO_ABS",
101762
+ "imageId": "imgctx_12345abcde", // or null
101763
+ "targetMisconception": "Specific misconception this question addresses (or 'none' if not applicable)",
101764
+ "difficultyReason": "Strategic explanation of difficulty choice and placement",
101765
+ "topicSpecificity": "broad|focused|specific",
101766
+ "originalLoId": "corresponding_LoId_from_input",
101767
+ "originalSubject": "corresponding_Subject_from_input",
101768
+ "originalCategory": "corresponding_Category_from_input",
101769
+ "originalTopic": "corresponding_Topic_from_input"
101770
+ }
101771
+ ],
101772
+ "diversityMetrics": {
101773
+ "questionTypeDistribution": {"type1": count1, "type2": count2},
101774
+ "bloomLevelDistribution": {"level1": count1, "level2": count2},
101775
+ "maxConsecutiveSameType": number,
101776
+ "difficultyProgression": "description of how difficulty progresses",
101777
+ "misconceptionCoverage": number_of_misconceptions_addressed
101778
+ },
101779
+ "planningStrategy": {
101780
+ "overallApproach": "Brief description of the strategic approach taken",
101781
+ "keyDecisions": ["Major decision 1", "Major decision 2", "Major decision 3"]
101782
+ }
101783
+ }
101784
+ \`\`\`
101785
+
101786
+ Execute this plan with pedagogical precision. The quiz should feel like a carefully crafted learning journey that challenges and educates simultaneously.`;
101787
+ logger.log("PROMPT_PREPARATION", {
101788
+ promptLength: enhancedPromptText.length,
101789
+ topicCount: clientInput.topics.length,
101790
+ misconceptionCount: clientInput.topics.reduce((sum, t4) => sum + (t4.commonMisconceptions?.length || 0), 0)
101791
+ }, Date.now() - promptStartTime);
101792
+ const generationStartTime = Date.now();
101793
+ const contents = [
101794
+ {
101795
+ role: "user",
101796
+ parts: [
101797
+ {
101798
+ text: enhancedPromptText
101799
+ }
101800
+ ]
101801
+ }
101802
+ ];
101803
+ const aiResult = await ai.models.generateContent({
101804
+ model,
101805
+ config: config3,
101806
+ contents
101807
+ });
101808
+ const response = aiResult;
101809
+ const generationDuration = Date.now() - generationStartTime;
101810
+ logger.log("AI_GENERATION", {
101811
+ responseLength: response.candidates?.[0]?.content?.parts?.[0]?.text?.length || 0,
101812
+ duration: generationDuration
101813
+ }, generationDuration);
101814
+ const processingStartTime = Date.now();
101815
+ const rawText = response.candidates?.[0]?.content?.parts?.[0]?.text || "";
101816
+ let jsonText = rawText;
101817
+ if (!rawText.trim().startsWith("{") && !rawText.trim().startsWith("[")) {
101818
+ jsonText = extractJsonFromMarkdown(rawText);
101819
+ }
101820
+ logger.log("JSON_EXTRACTION", {
101821
+ rawTextLength: rawText.length,
101822
+ extractedJsonLength: jsonText.length
101823
+ });
101824
+ const aiGeneratedContent = GenerateQuizPlanOutputSchema.parse(JSON.parse(jsonText));
101825
+ logger.log("SCHEMA_VALIDATION", { success: true }, Date.now() - processingStartTime);
101826
+ const validationStartTime = Date.now();
101827
+ if (aiGeneratedContent.quizPlan.length !== clientInput.totalQuestions) {
101828
+ throw new Error(`AI planned for ${aiGeneratedContent.quizPlan.length} questions, but ${clientInput.totalQuestions} were requested.`);
101829
+ }
101830
+ const invalidTypes = [];
101831
+ aiGeneratedContent.quizPlan.forEach((item, index3) => {
101832
+ if (!clientInput.selectedQuestionTypes.includes(item.plannedQuestionType)) {
101833
+ invalidTypes.push(`Question ${index3 + 1}: '${item.plannedQuestionType}'`);
101834
+ }
101835
+ });
101836
+ if (invalidTypes.length > 0) {
101837
+ throw new Error(`Invalid question types found: ${invalidTypes.join(", ")}`);
101838
+ }
101839
+ const codingQuestions = aiGeneratedContent.quizPlan.filter((q2) => q2.plannedQuestionType === "coding");
101840
+ if (numCodingQuestions > 0 && codingQuestions.length !== numCodingQuestions) {
101841
+ throw new Error(`Expected ${numCodingQuestions} coding questions, but got ${codingQuestions.length}`);
101842
+ }
101843
+ const diversityAnalysis = validateConsecutiveTypes(aiGeneratedContent.quizPlan);
101844
+ logger.log("VALIDATION_COMPLETE", {
101845
+ questionCount: aiGeneratedContent.quizPlan.length,
101846
+ codingQuestionCount: codingQuestions.length,
101847
+ maxConsecutiveType: diversityAnalysis.maxConsecutive,
101848
+ questionTypeDistribution: aiGeneratedContent.diversityMetrics?.questionTypeDistribution || {}
101849
+ }, Date.now() - validationStartTime);
101850
+ const finalResult = {
101851
+ ...aiGeneratedContent,
101852
+ logs: logger.getLogs()
101853
+ };
101854
+ logger.log("GENERATION_COMPLETE", {
101855
+ totalDuration: logger.getTotalDuration(),
101856
+ success: true,
101857
+ finalQuestionCount: finalResult.quizPlan.length
101858
+ }, logger.getTotalDuration());
101859
+ console.log("\n=== QUIZ PLAN GENERATION SUMMARY ===");
101860
+ console.log(`\u2705 Successfully generated ${finalResult.quizPlan.length} questions`);
101861
+ console.log(`\u23F1\uFE0F Total generation time: ${logger.getTotalDuration()}ms`);
101862
+ console.log(`\u{1F3AF} Question types: ${Object.keys(finalResult.diversityMetrics?.questionTypeDistribution || {}).join(", ")}`);
101863
+ console.log(`\u{1F9E0} Bloom levels: ${Object.keys(finalResult.diversityMetrics?.bloomLevelDistribution || {}).join(", ")}`);
101864
+ if (numCodingQuestions > 0) {
101865
+ console.log(`\u{1F4BB} Coding questions: ${codingQuestions.length}/${numCodingQuestions} required`);
101866
+ }
101867
+ console.log(JSON.stringify(finalResult));
101868
+ console.log("=====================================\n");
101869
+ return finalResult;
101870
+ } catch (error) {
101871
+ logger.log("ERROR", {
101872
+ message: error.message,
101873
+ stack: error.stack,
101874
+ totalDuration: logger.getTotalDuration()
101875
+ });
101876
+ console.error("\u274C Quiz Plan Generation Failed:", error.message);
101877
+ console.error("\u{1F4CB} Full logs available in returned object");
101878
+ throw new Error(`Failed to generate Quiz Plan: ${error.message}`);
101879
+ }
101880
+ }
101881
+ function validateConsecutiveTypes(quizPlan) {
101882
+ let maxConsecutive = 1;
101883
+ let maxType = quizPlan[0]?.plannedQuestionType || "";
101884
+ let maxStartIndex = 0;
101885
+ let currentConsecutive = 1;
101886
+ let currentType = quizPlan[0]?.plannedQuestionType || "";
101887
+ let currentStartIndex = 0;
101888
+ for (let i2 = 1; i2 < quizPlan.length; i2++) {
101889
+ if (quizPlan[i2].plannedQuestionType === currentType) {
101890
+ currentConsecutive++;
101891
+ } else {
101892
+ if (currentConsecutive > maxConsecutive) {
101893
+ maxConsecutive = currentConsecutive;
101894
+ maxType = currentType;
101895
+ maxStartIndex = currentStartIndex;
101896
+ }
101897
+ currentConsecutive = 1;
101898
+ currentType = quizPlan[i2].plannedQuestionType;
101899
+ currentStartIndex = i2;
101900
+ }
101901
+ }
101902
+ if (currentConsecutive > maxConsecutive) {
101903
+ maxConsecutive = currentConsecutive;
101904
+ maxType = currentType;
101905
+ maxStartIndex = currentStartIndex;
101906
+ }
101907
+ return {
101908
+ maxConsecutive,
101909
+ type: maxType,
101910
+ startIndex: maxStartIndex
101911
+ };
101912
+ }
101913
+
101914
+ // src/ai/flows/generate-questions-from-quiz-plan.ts
101915
+ init_react_shim();
101916
+
101917
+ // src/services/TopicDataService.ts
101918
+ init_react_shim();
101919
+ var TopicDataService = class {
101920
+ static saveData(data) {
101921
+ try {
101922
+ if (typeof window === "undefined") return;
101923
+ const serializedData = JSON.stringify(data);
101924
+ localStorage.setItem(this.STORAGE_KEY, serializedData);
101925
+ } catch (error) {
101926
+ console.error("Error saving learning objectives to Local Storage:", error);
101927
+ }
101928
+ }
101929
+ static mergeData(newData) {
101930
+ const existingData = this.getData();
101931
+ const loMap = new Map(existingData.map((lo) => [lo.code, lo]));
101932
+ newData.forEach((newLo) => {
101933
+ loMap.set(newLo.code, newLo);
101934
+ });
101935
+ const mergedData = Array.from(loMap.values());
101936
+ this.saveData(mergedData);
101937
+ }
101938
+ static getData() {
101939
+ try {
101940
+ if (typeof window === "undefined") return [];
101941
+ const storedData = localStorage.getItem(this.STORAGE_KEY);
101942
+ return storedData ? JSON.parse(storedData) : [];
101943
+ } catch (error) {
101944
+ console.error("Error retrieving learning objectives from Local Storage:", error);
101945
+ this.clearData();
101946
+ return [];
101947
+ }
101948
+ }
101949
+ static clearData() {
101950
+ try {
101951
+ if (typeof window === "undefined") return;
101952
+ localStorage.removeItem(this.STORAGE_KEY);
101953
+ } catch (error) {
101954
+ console.error("Error clearing learning objectives from Local Storage:", error);
101955
+ }
101956
+ }
101957
+ static parseTSV(tsvContent) {
101958
+ const lines = tsvContent.split("\n").filter((line) => line.trim() !== "");
101959
+ if (lines.length < 2) {
101960
+ return { data: [], errors: ["File is empty or contains only a header."] };
101961
+ }
101962
+ const headerLine = lines.shift();
101963
+ const headers = headerLine.split(" ").map((h3) => h3.trim());
101964
+ if (headers.length !== this.EXPECTED_HEADERS.length || !this.EXPECTED_HEADERS.every((h3, i2) => h3 === headers[i2])) {
101965
+ const errorMsg = `Invalid TSV header. Expected: "${this.EXPECTED_HEADERS.join(" ")}". Received: "${headers.join(" ")}"`;
101966
+ return { data: [], errors: [errorMsg] };
101967
+ }
101968
+ const data = [];
101969
+ const errors2 = [];
101970
+ lines.forEach((line, index3) => {
101971
+ const values = line.split(" ").map((v) => v.trim());
101972
+ if (values.length !== this.EXPECTED_HEADERS.length) {
101973
+ errors2.push(`Line ${index3 + 2}: Incorrect number of columns. Expected ${this.EXPECTED_HEADERS.length}, but got ${values.length}.`);
101974
+ return;
101975
+ }
101976
+ const [
101977
+ loId,
101978
+ name3,
101979
+ loDescription,
101980
+ subject,
101981
+ category,
101982
+ topic,
101983
+ keywordsStr,
101984
+ grade,
101985
+ stemElementsStr,
101986
+ bloomLevelsStr
101987
+ ] = values;
101988
+ if (!loId || !loDescription || !subject || !category || !topic) {
101989
+ errors2.push(`Line ${index3 + 2}: Missing required fields (LO ID, LO Description, Subject, Category, or Topic).`);
101990
+ return;
101991
+ }
101992
+ const learningObjective = {
101993
+ id: generateUniqueId("lo_"),
101994
+ code: loId,
101995
+ name: name3,
101996
+ description: loDescription,
101997
+ // Can be the same as name or enhanced later
101998
+ subject,
101999
+ category,
102000
+ topic,
102001
+ grade,
102002
+ keywords: keywordsStr.split(",").map((k3) => k3.trim()).filter(Boolean),
102003
+ stemElements: stemElementsStr.split(",").map((s4) => s4.trim()).filter(Boolean),
102004
+ bloomLevelsGuideline: bloomLevelsStr.split(",").map((b2) => b2.trim()).filter(Boolean)
102005
+ };
102006
+ data.push(learningObjective);
102007
+ });
102008
+ return { data, errors: errors2 };
102009
+ }
102010
+ static getSubjects() {
102011
+ const data = this.getData();
102012
+ const subjects = data.map((item) => item.subject);
102013
+ return [...new Set(subjects)].sort();
102014
+ }
102015
+ static getCategoriesBySubject(subject) {
102016
+ const data = this.getData();
102017
+ const categories = data.filter((item) => item.subject === subject).map((item) => item.category);
102018
+ return [...new Set(categories)].sort();
102019
+ }
102020
+ static getTopicsByCategory(category) {
102021
+ const data = this.getData();
102022
+ const topics = data.filter((item) => item.category === category).map((item) => item.topic);
102023
+ return [...new Set(topics)].sort();
102024
+ }
102025
+ static getLearningObjectivesByTopics(topics) {
102026
+ const data = this.getData();
102027
+ const topicSet = new Set(topics);
102028
+ return data.filter((item) => topicSet.has(item.topic));
102029
+ }
102030
+ };
102031
+ TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
102032
+ TopicDataService.EXPECTED_HEADERS = [
102033
+ "LO ID",
102034
+ "LO Description",
102035
+ "Subject",
102036
+ "Category",
102037
+ "Topic",
102038
+ "Keywords",
102039
+ "Grade",
102040
+ "STEM Element(s)",
102041
+ "Bloom\u2019s Level(s) Guideline"
102042
+ ];
102043
+
102029
102044
  // src/ai/flows/question-gen/generate-coding-question.ts
102030
102045
  init_react_shim();
102031
102046
 
@@ -102063,7 +102078,7 @@ Previous attempts failed. Pay strict attention to the JSON schema and all rules.
102063
102078
  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.` : "";
102064
102079
  const contextStrings = [
102065
102080
  `**Subject:** ${subject}`,
102066
- quizContext?.loDescription && `**Learning Objective:** ${quizContext.loDescription}`,
102081
+ quizContext?.description && `**Learning Objective:** ${quizContext.description}`,
102067
102082
  imageContextInstruction,
102068
102083
  quizContext?.plannedBloomLevel && `**Cognitive Level (Bloom's):** ${quizContext.plannedBloomLevel}`,
102069
102084
  quizContext?.targetMisconception && `**Target Misconception:** The problem should test against this common error: "${quizContext.targetMisconception}"`
@@ -102220,9 +102235,9 @@ var calculateCombinedDifficulty = (plannedQ) => {
102220
102235
  break;
102221
102236
  }
102222
102237
  const totalScore = bloomScore + contextScore + questionTypeScore;
102223
- if (totalScore <= 4) return "easy";
102224
- if (totalScore <= 7) return "medium";
102225
- return "hard";
102238
+ if (totalScore <= 4) return "Easy";
102239
+ if (totalScore <= 7) return "Medium";
102240
+ return "Hard";
102226
102241
  };
102227
102242
  async function generateQuestionsFromQuizPlan(clientInput, apiKey) {
102228
102243
  const { quizPlan, language: language3, imageContexts } = clientInput;
@@ -102235,7 +102250,7 @@ async function generateQuestionsFromQuizPlan(clientInput, apiKey) {
102235
102250
  let lastError = null;
102236
102251
  for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
102237
102252
  try {
102238
- const fullLO = plannedQ.originalLoId ? allLearningObjectives.find((lo) => lo.loId === plannedQ.originalLoId) : null;
102253
+ const fullLO = plannedQ.originalLoId ? allLearningObjectives.find((lo) => lo.code === plannedQ.originalLoId) : null;
102239
102254
  const quizContext = {
102240
102255
  plannedTopic: plannedQ.plannedTopic,
102241
102256
  plannedQuestionType: plannedQ.plannedQuestionType,
@@ -102248,7 +102263,7 @@ async function generateQuestionsFromQuizPlan(clientInput, apiKey) {
102248
102263
  originalSubject: plannedQ.originalSubject,
102249
102264
  originalCategory: plannedQ.originalCategory,
102250
102265
  originalTopic: plannedQ.originalTopic,
102251
- loDescription: fullLO?.loDescription || plannedQ.plannedTopic
102266
+ description: fullLO?.description || plannedQ.plannedTopic
102252
102267
  };
102253
102268
  const imageUrl = plannedQ.imageId && imageContexts ? imageContexts.find((ctx) => ctx.id === plannedQ.imageId)?.imageUrl : void 0;
102254
102269
  const baseClientInput = {
@@ -102602,7 +102617,7 @@ var AIFullQuizGeneratorModal = ({
102602
102617
  };
102603
102618
  const renderContent3 = () => {
102604
102619
  if (currentStage === "review") {
102605
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("h3", { className: "text-lg font-medium mb-2 text-primary flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Eye, { className: "mr-2 h-5 w-5" }), " Review & Adjust AI Generated Quiz Plan"), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, "The AI has proposed the following plan. You can change question types, reorder, or remove questions before final generation. Note: 'Drag and Drop' type questions in the plan will be created as placeholders and require manual authoring of items/zones/answers."), aiGeneratedPlan && aiGeneratedPlan.length > 0 ? /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "max-h-[calc(60vh - 120px)] pr-3" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, aiGeneratedPlan.map((plannedQ, index3) => /* @__PURE__ */ React169__namespace.default.createElement(Card, { key: `planned-${index3}-${plannedQ.plannedTopic.replace(/\s/g, "")}`, className: "p-3" }, /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "p-0 space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold text-sm" }, "Q", index3 + 1, ": ", plannedQ.plannedTopic), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex space-x-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "up"), disabled: index3 === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(ArrowUp, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "down"), disabled: index3 === aiGeneratedPlan.length - 1 }, /* @__PURE__ */ React169__namespace.default.createElement(ArrowDown, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7 text-destructive", onClick: () => handleRemovePlannedQuestion(index3) }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-3 items-end" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `review-qtype-${index3}`, className: "text-xs" }, "Question Type"), /* @__PURE__ */ React169__namespace.default.createElement(
102620
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("h3", { className: "text-lg font-Medium mb-2 text-primary flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Eye, { className: "mr-2 h-5 w-5" }), " Review & Adjust AI Generated Quiz Plan"), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, "The AI has proposed the following plan. You can change question types, reorder, or remove questions before final generation. Note: 'Drag and Drop' type questions in the plan will be created as placeholders and require manual authoring of items/zones/answers."), aiGeneratedPlan && aiGeneratedPlan.length > 0 ? /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "max-h-[calc(60vh - 120px)] pr-3" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, aiGeneratedPlan.map((plannedQ, index3) => /* @__PURE__ */ React169__namespace.default.createElement(Card, { key: `planned-${index3}-${plannedQ.plannedTopic.replace(/\s/g, "")}`, className: "p-3" }, /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "p-0 space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold text-sm" }, "Q", index3 + 1, ": ", plannedQ.plannedTopic), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex space-x-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "up"), disabled: index3 === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(ArrowUp, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleMovePlannedQuestion(index3, "down"), disabled: index3 === aiGeneratedPlan.length - 1 }, /* @__PURE__ */ React169__namespace.default.createElement(ArrowDown, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7 text-destructive", onClick: () => handleRemovePlannedQuestion(index3) }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-3 items-end" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `review-qtype-${index3}`, className: "text-xs" }, "Question Type"), /* @__PURE__ */ React169__namespace.default.createElement(
102606
102621
  Select2,
102607
102622
  {
102608
102623
  value: plannedQ.plannedQuestionType,
@@ -102631,7 +102646,7 @@ var AIFullQuizGeneratorModal = ({
102631
102646
  min: "1",
102632
102647
  max: "100"
102633
102648
  }
102634
- )), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-medium px-1" }, "Topic Distribution"), topics.map((topicItem, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: topicItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
102649
+ )), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Topic Distribution"), topics.map((topicItem, index3) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: topicItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
102635
102650
  Input,
102636
102651
  {
102637
102652
  type: "text",
@@ -102651,7 +102666,7 @@ var AIFullQuizGeneratorModal = ({
102651
102666
  min: "0",
102652
102667
  max: "100"
102653
102668
  }
102654
- ), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeTopic(topicItem.id), disabled: topics.length <= 1 }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: addTopic }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic")), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-medium px-1" }, "Bloom Level Distribution"), bloomLevels.map((bloomItem) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: bloomItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "w-32 capitalize flex-shrink-0" }, bloomItem.level), /* @__PURE__ */ React169__namespace.default.createElement(
102669
+ ), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeTopic(topicItem.id), disabled: topics.length <= 1 }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", size: "sm", onClick: addTopic }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic")), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Bloom Level Distribution"), bloomLevels.map((bloomItem) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: bloomItem.id, className: "flex items-center gap-2 mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "w-32 capitalize flex-shrink-0" }, bloomItem.level), /* @__PURE__ */ React169__namespace.default.createElement(
102655
102670
  Input,
102656
102671
  {
102657
102672
  type: "number",
@@ -102662,7 +102677,7 @@ var AIFullQuizGeneratorModal = ({
102662
102677
  min: "0",
102663
102678
  max: "100"
102664
102679
  }
102665
- )))), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-medium px-1" }, "Relevant Contexts (Optional, Select Multiple)"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, contextOptions.filter((opt) => opt.contextId !== "__none__" && opt.contextId !== "__custom__").map((opt) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: opt.contextId, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
102680
+ )))), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Relevant Contexts (Optional, Select Multiple)"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, contextOptions.filter((opt) => opt.contextId !== "__none__" && opt.contextId !== "__custom__").map((opt) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: opt.contextId, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
102666
102681
  Checkbox2,
102667
102682
  {
102668
102683
  id: `ctx-full-${opt.contextId}`,
@@ -102684,7 +102699,7 @@ var AIFullQuizGeneratorModal = ({
102684
102699
  placeholder: "Enter your specific custom context here...",
102685
102700
  className: "min-h-[60px] mt-2 text-sm"
102686
102701
  }
102687
- )), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-medium px-1" }, "Question Types to Use (Select Multiple)"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: qType.value, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
102702
+ )), /* @__PURE__ */ React169__namespace.default.createElement("fieldset", { className: "border p-3 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("legend", { className: "text-sm font-Medium px-1" }, "Question Types to Use (Select Multiple)"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-2 mt-2 max-h-40 overflow-y-auto" }, availableQuestionTypesForFullQuiz.map((qType) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: qType.value, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(
102688
102703
  Checkbox2,
102689
102704
  {
102690
102705
  id: `qtype-full-${qType.value}`,
@@ -103084,7 +103099,7 @@ var AlertTitle = React169__namespace.forwardRef(({ className, ...props }, ref) =
103084
103099
  "h5",
103085
103100
  {
103086
103101
  ref,
103087
- className: cn("mb-1 font-medium leading-none tracking-tight", className),
103102
+ className: cn("mb-1 font-Medium leading-none tracking-tight", className),
103088
103103
  ...props
103089
103104
  }
103090
103105
  ));
@@ -103129,7 +103144,7 @@ var BaseRawQuestionSchema = zod.z.object({
103129
103144
  points: zod.z.number().optional(),
103130
103145
  explanation: zod.z.string().optional(),
103131
103146
  topic: zod.z.string().optional(),
103132
- difficulty: zod.z.enum(["easy", "medium", "hard"]).optional(),
103147
+ difficulty: zod.z.enum(["Easy", "Medium", "Hard"]).optional(),
103133
103148
  bloomLevel: zod.z.string().optional()
103134
103149
  });
103135
103150
  var RawMCQSchema = BaseRawQuestionSchema.extend({
@@ -103482,7 +103497,7 @@ var QuizEditorService = class {
103482
103497
  questionType: type,
103483
103498
  prompt: "",
103484
103499
  points: 10,
103485
- difficulty: "medium"
103500
+ difficulty: "Medium"
103486
103501
  };
103487
103502
  switch (type) {
103488
103503
  case "true_false":
@@ -103709,7 +103724,7 @@ var SelectedQuestionsPanel = ({
103709
103724
  },
103710
103725
  /* @__PURE__ */ React169__namespace.default.createElement(ArrowDown, { className: "h-3 w-3" })
103711
103726
  )),
103712
- /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "text-xs text-muted-foreground mt-1" }, q2.questionType, " \u2022 ", q2.points || 0, " pts")),
103727
+ /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "text-xs text-muted-foreground mt-1" }, q2.questionType, " \u2022 ", q2.points || 0, " pts")),
103713
103728
  /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-1 flex-shrink-0" }, /* @__PURE__ */ React169__namespace.default.createElement(
103714
103729
  Button,
103715
103730
  {
@@ -104096,7 +104111,7 @@ var TableFooter = React169__namespace.forwardRef(({ className, ...props }, ref)
104096
104111
  {
104097
104112
  ref,
104098
104113
  className: cn(
104099
- "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
104114
+ "border-t bg-muted/50 font-Medium [&>tr]:last:border-b-0",
104100
104115
  className
104101
104116
  ),
104102
104117
  ...props
@@ -104120,7 +104135,7 @@ var TableHead = React169__namespace.forwardRef(({ className, ...props }, ref) =>
104120
104135
  {
104121
104136
  ref,
104122
104137
  className: cn(
104123
- "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
104138
+ "h-12 px-4 text-left align-middle font-Medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
104124
104139
  className
104125
104140
  ),
104126
104141
  ...props
@@ -104210,20 +104225,6 @@ var questionTypeManager = new LocalStorageManager("question_types");
104210
104225
  var learningObjectiveManager = new LocalStorageManager("learning_objectives");
104211
104226
  var contextManager = new LocalStorageManager("contexts");
104212
104227
  var approachManager = new LocalStorageManager("approaches");
104213
- function mapRawDifficultyToStandard(rawDifficulty) {
104214
- switch (rawDifficulty) {
104215
- case "E":
104216
- case "E~M":
104217
- return "easy";
104218
- case "M":
104219
- case "M~H":
104220
- return "medium";
104221
- case "H":
104222
- return "hard";
104223
- default:
104224
- return "medium";
104225
- }
104226
- }
104227
104228
  var MetadataService = class {
104228
104229
  };
104229
104230
  // --- Subject Services ---
@@ -104281,15 +104282,10 @@ MetadataService.deleteContext = (code4) => contextManager.delete(code4);
104281
104282
  MetadataService.getApproaches = () => approachManager.getAll().sort((a4, b2) => a4.code.localeCompare(b2.code));
104282
104283
  MetadataService.saveApproaches = (items) => approachManager.saveAll(items);
104283
104284
  MetadataService.addApproach = (approachData) => {
104284
- const difficulty = mapRawDifficultyToStandard(approachData.rawDifficulty);
104285
- return approachManager.add({ ...approachData, difficulty });
104285
+ return approachManager.add(approachData);
104286
104286
  };
104287
104287
  MetadataService.updateApproach = (id3, approachData) => {
104288
- const updates = { ...approachData };
104289
- if (approachData.rawDifficulty) {
104290
- updates.difficulty = mapRawDifficultyToStandard(approachData.rawDifficulty);
104291
- }
104292
- return approachManager.update(id3, updates);
104288
+ return approachManager.update(id3, approachData);
104293
104289
  };
104294
104290
  MetadataService.deleteApproach = (code4) => approachManager.delete(code4);
104295
104291
  // --- LearningObjective Services ---
@@ -104299,8 +104295,12 @@ MetadataService.getLearningObjectives = (subjectCode) => {
104299
104295
  return filtered.sort((a4, b2) => a4.name.localeCompare(b2.name));
104300
104296
  };
104301
104297
  MetadataService.saveLearningObjectives = (items) => learningObjectiveManager.saveAll(items);
104302
- MetadataService.addLearningObjective = (name3, code4, subjectCode, description) => learningObjectiveManager.add({ name: name3, code: code4, subjectCode, description });
104303
- MetadataService.updateLearningObjective = (id3, name3, code4, subjectCode, description) => learningObjectiveManager.update(id3, { name: name3, code: code4, subjectCode, description });
104298
+ MetadataService.addLearningObjective = (item) => {
104299
+ return learningObjectiveManager.add(item);
104300
+ };
104301
+ MetadataService.updateLearningObjective = (id3, updates) => {
104302
+ return learningObjectiveManager.update(id3, updates);
104303
+ };
104304
104304
  MetadataService.deleteLearningObjective = (code4) => learningObjectiveManager.delete(code4);
104305
104305
 
104306
104306
  // node_modules/date-fns/addDays.mjs
@@ -106200,7 +106200,7 @@ function QuestionList({
106200
106200
  if (questions.length === 0) {
106201
106201
  return /* @__PURE__ */ React169__namespace.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.");
106202
106202
  }
106203
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "w-[30%]" }, "Question Text"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Type"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Topic"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Grade"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Bloom's"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Last Modified"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, questions.map((question2) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: question2.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium max-w-xs truncate", title: question2.text }, /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: question2.questionConfig.prompt })), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, question2.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, /* @__PURE__ */ React169__namespace.default.createElement(Badge2, { variant: "secondary" }, getLookupName(question2.questionTypeCode, metadata.questionTypes))), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, getLookupName(question2.subjectCode, metadata.subjects)), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, getLookupName(question2.topicCode, metadata.topics)), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, getLookupName(question2.gradeLevelCode, metadata.gradeLevels)), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, /* @__PURE__ */ React169__namespace.default.createElement(Badge2, { variant: "outline" }, getLookupName(question2.bloomLevelCode, metadata.bloomLevels))), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, format(new Date(question2.lastModified), "MMM d, yyyy")), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, onView && /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onView(question2), className: "mr-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Eye, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onEdit(question2), className: "mr-1" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onDelete(question2), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" }))))))));
106203
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "w-[30%]" }, "Question Text"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Type"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Topic"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Grade"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Bloom's"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Last Modified"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, questions.map((question2) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: question2.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium max-w-xs truncate", title: question2.text }, /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: question2.questionConfig.prompt })), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, question2.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, /* @__PURE__ */ React169__namespace.default.createElement(Badge2, { variant: "secondary" }, getLookupName(question2.questionTypeCode, metadata.questionTypes))), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, getLookupName(question2.subjectCode, metadata.subjects)), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, getLookupName(question2.topicCode, metadata.topics)), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, getLookupName(question2.gradeLevelCode, metadata.gradeLevels)), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, /* @__PURE__ */ React169__namespace.default.createElement(Badge2, { variant: "outline" }, getLookupName(question2.bloomLevelCode, metadata.bloomLevels))), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, format(new Date(question2.lastModified), "MMM d, yyyy")), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, onView && /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onView(question2), className: "mr-1" }, /* @__PURE__ */ React169__namespace.default.createElement(Eye, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onEdit(question2), className: "mr-1" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onDelete(question2), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" }))))))));
106204
106204
  }
106205
106205
 
106206
106206
  // src/react-ui/components/authoring/QuestionFilters.tsx
@@ -106281,7 +106281,7 @@ function QuestionFilters({
106281
106281
  onChange: (e3) => setSearchTerm(e3.target.value),
106282
106282
  className: "lg:col-span-2 xl:col-span-1"
106283
106283
  }
106284
- ), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: subjectCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setSubjectCode) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Subject" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Subjects"), subjects.map((s4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: s4.code, value: s4.code }, s4.name)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: topicCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setTopicCode), disabled: !subjectCode || filteredTopics.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Topic" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Topics"), filteredTopics.map((t4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: t4.code, value: t4.code }, t4.name)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: gradeLevelCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setGradeLevelCode) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Grade Level" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Grade Levels"), gradeLevels.map((gl) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: gl.code, value: gl.code }, gl.name)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: bloomLevelCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setBloomLevelCode) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Bloom's Level" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Levels"), bloomLevels.map((bl) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: bl.code, value: bl.code }, bl.name)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: questionTypeCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setQuestionTypeCode) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Question Type" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Types"), questionTypes.map((qt) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: qt.code, value: qt.code }, qt.name)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: difficulty || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setDifficulty) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Difficulty" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Difficulties"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "easy" }, "Easy"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "medium" }, "Medium"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "hard" }, "Hard"))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex gap-2 col-span-full sm:col-span-1 xl:col-span-2 xl:col-start-6" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleApplyFilters, className: "w-full sm:w-auto flex-grow" }, /* @__PURE__ */ React169__namespace.default.createElement(Search, { className: "mr-2 h-4 w-4" }), " Apply"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleClearFilters, variant: "outline", className: "w-full sm:w-auto flex-grow" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleX, { className: "mr-2 h-4 w-4" }), " Clear"))));
106284
+ ), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: subjectCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setSubjectCode) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Subject" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Subjects"), subjects.map((s4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: s4.code, value: s4.code }, s4.name)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: topicCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setTopicCode), disabled: !subjectCode || filteredTopics.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Topic" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Topics"), filteredTopics.map((t4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: t4.code, value: t4.code }, t4.name)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: gradeLevelCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setGradeLevelCode) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Grade Level" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Grade Levels"), gradeLevels.map((gl) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: gl.code, value: gl.code }, gl.name)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: bloomLevelCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setBloomLevelCode) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Bloom's Level" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Levels"), bloomLevels.map((bl) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: bl.code, value: bl.code }, bl.name)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: questionTypeCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setQuestionTypeCode) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Question Type" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Types"), questionTypes.map((qt) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: qt.code, value: qt.code }, qt.name)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: difficulty || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setDifficulty) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Difficulty" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: ALL_ITEMS_VALUE }, "All Difficulties"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "Easy" }, "Easy"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "Medium" }, "Medium"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "Hard" }, "Hard"))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex gap-2 col-span-full sm:col-span-1 xl:col-span-2 xl:col-start-6" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleApplyFilters, className: "w-full sm:w-auto flex-grow" }, /* @__PURE__ */ React169__namespace.default.createElement(Search, { className: "mr-2 h-4 w-4" }), " Apply"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleClearFilters, variant: "outline", className: "w-full sm:w-auto flex-grow" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleX, { className: "mr-2 h-4 w-4" }), " Clear"))));
106285
106285
  }
106286
106286
 
106287
106287
  // src/react-ui/components/authoring/QuestionFormDialog.tsx
@@ -106389,7 +106389,7 @@ function QuestionFormDialog({
106389
106389
  };
106390
106390
  const dialogTitle = questionToEdit ? "Edit Question Metadata" : "Create New Question";
106391
106391
  const dialogDescription = "First, define the metadata for the question. Then, edit the question's content and logic.";
106392
- return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-xl md:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, dialogTitle), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, dialogDescription)), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "code" }, "Question Code"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "gradeLevelCode" }, "Grade Level"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: gradeLevelCode, onValueChange: setGradeLevelCode }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "gradeLevelCode" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select Grade Level" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, gradeLevels.map((gl) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: gl.code, value: gl.code }, gl.name)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: subjectCode, onValueChange: setSubjectCode }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "subjectCode" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select Subject" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, subjects.map((s4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: s4.code, value: s4.code }, s4.name))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "topicCode" }, "Topic"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: topicCode, onValueChange: setTopicCode, disabled: !subjectCode || filteredTopics.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "topicCode" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select Topic" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, filteredTopics.map((t4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: t4.code, value: t4.code }, t4.name)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "bloomLevelCode" }, "Bloom's Level"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: bloomLevelCode, onValueChange: setBloomLevelCode }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "bloomLevelCode" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select Bloom's Level" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, bloomLevels.map((bl) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: bl.code, value: bl.code }, bl.name))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Question Content & Logic"), questionConfig ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-3 mt-2 border rounded-md bg-muted/30 flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-medium" }, "Type: ", /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-normal" }, questionConfig.questionType)), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground truncate max-w-md" }, "Prompt: ", questionConfig.prompt.replace(/<[^>]*>?/gm, "") || "Not set")), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "outline", onClick: () => handleOpenQuestionEditor() }, /* @__PURE__ */ React169__namespace.default.createElement(SquarePen, { className: "mr-2 h-4 w-4" }), " Edit Content")) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-3 mt-2 border-dashed border-2 rounded-md text-center" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground mb-2" }, "No content has been created yet."), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "default", onClick: () => handleOpenQuestionEditor("multiple_choice") }, /* @__PURE__ */ React169__namespace.default.createElement(BookCopy, { className: "mr-2 h-4 w-4" }), " Create Question Content")))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !questionConfig }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save Question")))), questionConfig && /* @__PURE__ */ React169__namespace.default.createElement(
106392
+ return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-xl md:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, dialogTitle), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, dialogDescription)), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "code" }, "Question Code"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "gradeLevelCode" }, "Grade Level"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: gradeLevelCode, onValueChange: setGradeLevelCode }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "gradeLevelCode" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select Grade Level" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, gradeLevels.map((gl) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: gl.code, value: gl.code }, gl.name)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: subjectCode, onValueChange: setSubjectCode }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "subjectCode" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select Subject" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, subjects.map((s4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: s4.code, value: s4.code }, s4.name))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "topicCode" }, "Topic"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: topicCode, onValueChange: setTopicCode, disabled: !subjectCode || filteredTopics.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "topicCode" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select Topic" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, filteredTopics.map((t4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: t4.code, value: t4.code }, t4.name)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "bloomLevelCode" }, "Bloom's Level"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: bloomLevelCode, onValueChange: setBloomLevelCode }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "bloomLevelCode" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select Bloom's Level" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, bloomLevels.map((bl) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: bl.code, value: bl.code }, bl.name))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { className: "font-semibold" }, "Question Content & Logic"), questionConfig ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-3 mt-2 border rounded-md bg-muted/30 flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-Medium" }, "Type: ", /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "font-normal" }, questionConfig.questionType)), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground truncate max-w-md" }, "Prompt: ", questionConfig.prompt.replace(/<[^>]*>?/gm, "") || "Not set")), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "outline", onClick: () => handleOpenQuestionEditor() }, /* @__PURE__ */ React169__namespace.default.createElement(SquarePen, { className: "mr-2 h-4 w-4" }), " Edit Content")) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-3 mt-2 border-dashed border-2 rounded-md text-center" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground mb-2" }, "No content has been created yet."), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "default", onClick: () => handleOpenQuestionEditor("multiple_choice") }, /* @__PURE__ */ React169__namespace.default.createElement(BookCopy, { className: "mr-2 h-4 w-4" }), " Create Question Content")))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !questionConfig }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save Question")))), questionConfig && /* @__PURE__ */ React169__namespace.default.createElement(
106393
106393
  EditQuestionModal,
106394
106394
  {
106395
106395
  isOpen: isQuestionEditorOpen,
@@ -110707,10 +110707,10 @@ var RoadmapService = class {
110707
110707
  }
110708
110708
  return null;
110709
110709
  }
110710
- static updateRoadmapItemStatus(loId, isCompleted) {
110710
+ static updateRoadmapItemStatus(code4, isCompleted) {
110711
110711
  const roadmap = this.getRoadmap();
110712
110712
  if (roadmap) {
110713
- const itemIndex = roadmap.items.findIndex((item) => item.loId === loId);
110713
+ const itemIndex = roadmap.items.findIndex((item) => item.code === code4);
110714
110714
  if (itemIndex > -1) {
110715
110715
  roadmap.items[itemIndex].isCompleted = isCompleted;
110716
110716
  this.saveRoadmap(roadmap);
@@ -111291,7 +111291,7 @@ zod.z.object({
111291
111291
  startDate: zod.z.string().describe("The start date for the analysis period in ISO format (YYYY-MM-DD)."),
111292
111292
  endDate: zod.z.string().describe("The end date for the analysis period in ISO format (YYYY-MM-DD)."),
111293
111293
  allAvailableTopics: zod.z.array(zod.z.object({
111294
- loId: zod.z.string(),
111294
+ code: zod.z.string(),
111295
111295
  subject: zod.z.string(),
111296
111296
  category: zod.z.string(),
111297
111297
  topic: zod.z.string()
@@ -111302,7 +111302,7 @@ var RoadmapItemSchema = zod.z.object({
111302
111302
  topicName: zod.z.string(),
111303
111303
  reason: zod.z.string(),
111304
111304
  suggestedDifficulty: zod.z.enum(["Very Easy", "Easy", "Medium", "Hard", "Expert"]),
111305
- loId: zod.z.string(),
111305
+ code: zod.z.string(),
111306
111306
  isCompleted: zod.z.boolean()
111307
111307
  });
111308
111308
  var WeeklyRoadmapSchema = zod.z.object({
@@ -111367,7 +111367,7 @@ All topic names and loIds in your "weeklyRoadmap" output MUST be chosen directly
111367
111367
  - **gamificationRemarks**: Write an encouraging message mentioning their achievements.
111368
111368
  2. **For "weeklyRoadmap":**
111369
111369
  - Create a 5-item roadmap for the upcoming week.
111370
- - Prioritize the "areasForImprovement" you identified. For each, find the corresponding entry in "All Available Topics" and use its "topicName" and "loId".
111370
+ - Prioritize the "areasForImprovement" you identified. For each, find the corresponding entry in "All Available Topics" and use its "topicName" and "code".
111371
111371
  - If more items are needed, select related topics from "All Available Topics".
111372
111372
 
111373
111373
  **IF Practice History IS EMPTY:**
@@ -111378,7 +111378,7 @@ All topic names and loIds in your "weeklyRoadmap" output MUST be chosen directly
111378
111378
  - **gamificationRemarks**: Write a general motivational message about starting to learn.
111379
111379
  2. **For "weeklyRoadmap":**
111380
111380
  - Create a 5-item "starter" roadmap.
111381
- - Select 5 diverse and foundational topics directly from the "All Available Topics" list. Use their exact "topicName" and "loId".
111381
+ - Select 5 diverse and foundational topics directly from the "All Available Topics" list. Use their exact "topicName" and "code".
111382
111382
  - For the "reason", explain that this is a good starting point to explore the subject.
111383
111383
 
111384
111384
  --- END LOGIC FLOW ---
@@ -111406,7 +111406,7 @@ The 'suggestedDifficulty' field MUST ALWAYS be one of these exact English string
111406
111406
  "topicName": "Topic C",
111407
111407
  "reason": "To strengthen your understanding of this key area.",
111408
111408
  "suggestedDifficulty": "Easy",
111409
- "loId": "lo-id-for-topic-c-from-the-list",
111409
+ "code": "lo-id-for-topic-c-from-the-list",
111410
111410
  "isCompleted": false
111411
111411
  }
111412
111412
  ]
@@ -111668,11 +111668,11 @@ init_react_shim();
111668
111668
  // src/ai/flows/assess-and-map-document-types.ts
111669
111669
  init_react_shim();
111670
111670
  var LearningObjectiveContextSchema = zod.z.object({
111671
- loId: zod.z.string(),
111671
+ code: zod.z.string(),
111672
111672
  subject: zod.z.string(),
111673
111673
  category: zod.z.string(),
111674
111674
  topic: zod.z.string(),
111675
- loDescription: zod.z.string()
111675
+ description: zod.z.string()
111676
111676
  });
111677
111677
  zod.z.object({
111678
111678
  language: zod.z.string().default("English"),
@@ -111680,7 +111680,7 @@ zod.z.object({
111680
111680
  learningObjectives: zod.z.array(LearningObjectiveContextSchema).min(1, { message: "At least one learning objective is required for mapping." })
111681
111681
  });
111682
111682
  var MappedLOSchema = zod.z.object({
111683
- loId: zod.z.string().describe("The exact loId from the provided learning objectives list that matches the document content."),
111683
+ code: zod.z.string().describe("The exact code from the provided learning objectives list that matches the document content."),
111684
111684
  confidence: zod.z.number().min(0).max(100).describe("A confidence score (0-100) of how well the document maps to this LO."),
111685
111685
  reasoning: zod.z.string().describe("A brief explanation for why this mapping is relevant.")
111686
111686
  });
@@ -111716,7 +111716,7 @@ You are an expert curriculum analyst. Your task is to analyze a given document a
111716
111716
  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).
111717
111717
 
111718
111718
  2. **Specific Mapping:** Identify which specific LOs from the list are directly addressed by the document. For each match you find, provide:
111719
- - The exact "loId" of the matched LO.
111719
+ - The exact "code" of the matched LO.
111720
111720
  - A "confidence" score (0-100) for that specific match.
111721
111721
  - A brief "reasoning" in ${language3} explaining why the document content maps to that LO.
111722
111722
 
@@ -111731,7 +111731,7 @@ Return a single, valid JSON object in this EXACT format. Do not include any othe
111731
111731
  "isFreestyleRecommended": false,
111732
111732
  "mappedLOs": [
111733
111733
  {
111734
- "loId": "SWIFT_FUNC_01",
111734
+ "code": "SWIFT_FUNC_01",
111735
111735
  "confidence": 95,
111736
111736
  "reasoning": "The document provides a detailed explanation of function syntax and default parameters, which directly aligns with this learning objective."
111737
111737
  }
@@ -111826,7 +111826,7 @@ Return the response as a single JSON object with a key "generatedQuestions" cont
111826
111826
  "correctTempOptionId": "A",
111827
111827
  "explanation": "The document states that mitochondria are the powerhouses of the cell, responsible for cellular respiration.",
111828
111828
  "points": 10,
111829
- "difficulty": "medium",
111829
+ "difficulty": "Medium",
111830
111830
  "topic": "Cell Biology"
111831
111831
  },
111832
111832
  {
@@ -111835,7 +111835,7 @@ Return the response as a single JSON object with a key "generatedQuestions" cont
111835
111835
  "correctAnswer": false,
111836
111836
  "explanation": "The text specifies that the cell wall is a feature of plant cells, not animal cells.",
111837
111837
  "points": 10,
111838
- "difficulty": "easy",
111838
+ "difficulty": "Easy",
111839
111839
  "topic": "Cell Biology"
111840
111840
  }
111841
111841
  ]
@@ -112668,7 +112668,7 @@ var QuizReview = ({
112668
112668
  };
112669
112669
  return /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "w-full max-w-4xl mx-auto shadow-xl" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-3xl font-headline text-center flex items-center justify-center" }, /* @__PURE__ */ React169__namespace.default.createElement(BookOpen, { className: "mr-3 h-8 w-8 text-primary" }), "AI-Powered Quiz Review"), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, { className: "text-center text-lg" }, "Let's break down your results and reinforce your learning.")), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "space-y-6" }, /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "bg-muted/30" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-xl flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Lightbulb, { className: "mr-2 h-5 w-5 text-yellow-500" }), "Key Concepts Summary")), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: reviewContent.overallSummary }))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("h3", { className: "text-xl font-semibold mb-2" }, "Detailed Question Analysis"), /* @__PURE__ */ React169__namespace.default.createElement(Accordion2, { type: "single", collapsible: true, className: "w-full" }, quizResult.questionResults.map((qResult, index3) => {
112670
112670
  const aiReview = getReviewForQuestion(qResult.questionId);
112671
- return /* @__PURE__ */ React169__namespace.default.createElement(AccordionItem2, { value: `item-${index3}`, key: qResult.questionId }, /* @__PURE__ */ React169__namespace.default.createElement(AccordionTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-between w-full pr-2" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-left font-medium" }, "Question ", index3 + 1), qResult.isCorrect ? /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-sm text-green-600 font-medium flex items-center gap-1" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleCheckBig, { className: "h-4 w-4" }), " Correct") : /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-sm text-destructive font-medium flex items-center gap-1" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleX, { className: "h-4 w-4" }), " Incorrect"))), /* @__PURE__ */ React169__namespace.default.createElement(AccordionContent2, { className: "space-y-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 border rounded-md bg-background" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold mb-2" }, "Original Question:"), /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: qResult.prompt })), qResult.questionType === "coding" ? renderCodingResult(qResult) : renderStandardResult(qResult), aiReview && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 border-l-4 border-primary bg-primary/10 rounded-r-md" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold text-primary mb-2" }, "AI Tutor Explanation:"), /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: aiReview.explanation }))));
112671
+ return /* @__PURE__ */ React169__namespace.default.createElement(AccordionItem2, { value: `item-${index3}`, key: qResult.questionId }, /* @__PURE__ */ React169__namespace.default.createElement(AccordionTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-between w-full pr-2" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-left font-Medium" }, "Question ", index3 + 1), qResult.isCorrect ? /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-sm text-green-600 font-Medium flex items-center gap-1" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleCheckBig, { className: "h-4 w-4" }), " Correct") : /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-sm text-destructive font-Medium flex items-center gap-1" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleX, { className: "h-4 w-4" }), " Incorrect"))), /* @__PURE__ */ React169__namespace.default.createElement(AccordionContent2, { className: "space-y-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 border rounded-md bg-background" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold mb-2" }, "Original Question:"), /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: qResult.prompt })), qResult.questionType === "coding" ? renderCodingResult(qResult) : renderStandardResult(qResult), aiReview && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 border-l-4 border-primary bg-primary/10 rounded-r-md" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold text-primary mb-2" }, "AI Tutor Explanation:"), /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: aiReview.explanation }))));
112672
112672
  }))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("h3", { className: "text-xl font-semibold mb-2" }, "Topics for Further Study"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-wrap gap-2" }, reviewContent.relatedTopics.map((topic, index3) => /* @__PURE__ */ React169__namespace.default.createElement(Badge2, { key: index3, variant: "secondary", className: "text-base px-3 py-1" }, topic))))), /* @__PURE__ */ React169__namespace.default.createElement(CardFooter, { className: "flex flex-col sm:flex-row justify-between gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "outline", onClick: onBackToResults, className: "w-full sm:w-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(ArrowLeft, { className: "mr-2 h-4 w-4" }), "Back to Results"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: onExit, className: "w-full sm:w-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(LogOut, { className: "mr-2 h-4 w-4" }), "Finish & Exit")));
112673
112673
  };
112674
112674
 
@@ -112715,7 +112715,7 @@ var PracticeHistoryTable = ({ history: history2, maxHeight = "400px" }) => {
112715
112715
  if (percentage >= 50) return "secondary";
112716
112716
  return "destructive";
112717
112717
  };
112718
- return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, null, t4("history.title")), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, null, t4("history.description"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "w-full border rounded-md", style: { height: maxHeight } }, /* @__PURE__ */ React169__namespace.default.createElement(TooltipProvider2, { delayDuration: 100 }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, { className: "sticky top-0 bg-muted z-10" }, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "w-[120px]" }, t4("history.headers.date")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, t4("history.headers.topic")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[80px]" }, t4("history.headers.score")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[90px]" }, t4("history.headers.percentage")))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, history2.length > 0 ? history2.map((session) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: session.id, onClick: () => handleRowClick(session), className: "cursor-pointer" }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium text-xs text-muted-foreground" }, formatDate(session.timestamp)), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col gap-1.5" }, session.topics.map((topicInfo, index3) => /* @__PURE__ */ React169__namespace.default.createElement(Tooltip2, { key: index3 }, /* @__PURE__ */ React169__namespace.default.createElement(TooltipTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold text-sm truncate" }, topicInfo.topic)), /* @__PURE__ */ React169__namespace.default.createElement(TooltipContent2, null, /* @__PURE__ */ React169__namespace.default.createElement("p", null, /* @__PURE__ */ React169__namespace.default.createElement("strong", null, t4("settingsModal.topics.tableHeaders.subject"), ":"), " ", topicInfo.subject), /* @__PURE__ */ React169__namespace.default.createElement("p", null, /* @__PURE__ */ React169__namespace.default.createElement("strong", null, t4("settingsModal.topics.tableHeaders.category"), ":"), " ", topicInfo.category)))))), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right font-mono text-sm" }, session.score !== null ? `${session.score}/${session.maxScore}` : "N/A"), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, session.percentage !== null && /* @__PURE__ */ React169__namespace.default.createElement(
112718
+ return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, null, t4("history.title")), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, null, t4("history.description"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "w-full border rounded-md", style: { height: maxHeight } }, /* @__PURE__ */ React169__namespace.default.createElement(TooltipProvider2, { delayDuration: 100 }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, { className: "sticky top-0 bg-muted z-10" }, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "w-[120px]" }, t4("history.headers.date")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, t4("history.headers.topic")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[80px]" }, t4("history.headers.score")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[90px]" }, t4("history.headers.percentage")))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, history2.length > 0 ? history2.map((session) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: session.id, onClick: () => handleRowClick(session), className: "cursor-pointer" }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium text-xs text-muted-foreground" }, formatDate(session.timestamp)), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col gap-1.5" }, session.topics.map((topicInfo, index3) => /* @__PURE__ */ React169__namespace.default.createElement(Tooltip2, { key: index3 }, /* @__PURE__ */ React169__namespace.default.createElement(TooltipTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-semibold text-sm truncate" }, topicInfo.topic)), /* @__PURE__ */ React169__namespace.default.createElement(TooltipContent2, null, /* @__PURE__ */ React169__namespace.default.createElement("p", null, /* @__PURE__ */ React169__namespace.default.createElement("strong", null, t4("settingsModal.topics.tableHeaders.subject"), ":"), " ", topicInfo.subject), /* @__PURE__ */ React169__namespace.default.createElement("p", null, /* @__PURE__ */ React169__namespace.default.createElement("strong", null, t4("settingsModal.topics.tableHeaders.category"), ":"), " ", topicInfo.category)))))), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right font-mono text-sm" }, session.score !== null ? `${session.score}/${session.maxScore}` : "N/A"), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, session.percentage !== null && /* @__PURE__ */ React169__namespace.default.createElement(
112719
112719
  Badge2,
112720
112720
  {
112721
112721
  variant: getPercentageBadgeVariant(session.percentage),
@@ -133530,12 +133530,12 @@ var ChartTooltipContent = React169__namespace.forwardRef(
133530
133530
  const itemConfig = getPayloadConfigFromPayload(config3, item, key);
133531
133531
  const value = !labelKey && typeof label === "string" ? config3[label]?.label || label : itemConfig?.label;
133532
133532
  if (labelFormatter) {
133533
- return /* @__PURE__ */ React169__namespace.createElement("div", { className: cn("font-medium", labelClassName) }, labelFormatter(value, payload));
133533
+ return /* @__PURE__ */ React169__namespace.createElement("div", { className: cn("font-Medium", labelClassName) }, labelFormatter(value, payload));
133534
133534
  }
133535
133535
  if (!value) {
133536
133536
  return null;
133537
133537
  }
133538
- return /* @__PURE__ */ React169__namespace.createElement("div", { className: cn("font-medium", labelClassName) }, value);
133538
+ return /* @__PURE__ */ React169__namespace.createElement("div", { className: cn("font-Medium", labelClassName) }, value);
133539
133539
  }, [
133540
133540
  label,
133541
133541
  labelFormatter,
@@ -133598,7 +133598,7 @@ var ChartTooltipContent = React169__namespace.forwardRef(
133598
133598
  )
133599
133599
  },
133600
133600
  /* @__PURE__ */ React169__namespace.createElement("div", { className: "grid gap-1.5" }, nestLabel ? tooltipLabel : null, /* @__PURE__ */ React169__namespace.createElement("span", { className: "text-muted-foreground" }, itemConfig?.label || item.name)),
133601
- item.value && /* @__PURE__ */ React169__namespace.createElement("span", { className: "font-mono font-medium tabular-nums text-foreground" }, item.value.toLocaleString())
133601
+ item.value && /* @__PURE__ */ React169__namespace.createElement("span", { className: "font-mono font-Medium tabular-nums text-foreground" }, item.value.toLocaleString())
133602
133602
  ))
133603
133603
  );
133604
133604
  }))
@@ -134775,7 +134775,7 @@ var ManageTopics = () => {
134775
134775
  return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Card, { className: "w-full max-w-4xl mx-auto shadow-none border-none" }, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, { className: "px-1" }, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex items-center text-2xl font-headline" }, /* @__PURE__ */ React169__namespace.default.createElement(FileText, { className: "mr-3 h-6 w-6 text-primary" }), t4("settingsModal.topics.title")), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, null, t4("settingsModal.topics.description"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "space-y-6 px-1" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "tsv-importer", className: "text-lg font-semibold" }, t4("settingsModal.topics.importData")), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "flex-grow" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("settingsModal.topics.importHint")), /* @__PURE__ */ React169__namespace.default.createElement("a", { href: "#", className: "text-xs text-primary hover:underline", onClick: (e3) => {
134776
134776
  e3.preventDefault();
134777
134777
  alert("Header format:\nLO ID LO Description Subject Category Topic Keywords Grade STEM Element(s) Bloom\u2019s Level(s) Guideline");
134778
- } }, t4("settingsModal.topics.viewHeaderFormat"))), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "tsv-importer", type: "file", ref: fileInputRef, accept: ".tsv,text/tab-separated-values", onChange: handleFileChange, className: "hidden" }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: () => fileInputRef.current?.click() }, /* @__PURE__ */ React169__namespace.default.createElement(Upload, { className: "mr-2 h-4 w-4" }), t4("settingsModal.topics.chooseFile")))), importErrors.length > 0 && /* @__PURE__ */ React169__namespace.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleAlert, { className: "h-4 w-4" }), /* @__PURE__ */ React169__namespace.default.createElement(AlertTitle, null, t4("settingsModal.topics.importErrors")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDescription, null, /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-24 mt-2" }, /* @__PURE__ */ React169__namespace.default.createElement("ul", { className: "list-disc list-inside text-xs space-y-1" }, importErrors.map((error, index3) => /* @__PURE__ */ React169__namespace.default.createElement("li", { key: index3 }, error)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement("h3", { className: "text-lg font-semibold" }, t4("settingsModal.topics.currentDataTitle")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("settingsModal.topics.showingCount", { shown: filteredLearningObjectives.length, total: learningObjectives.length }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subject-filter" }, t4("settingsModal.topics.filterBySubject")), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedSubjectFilter, onValueChange: setSelectedSubjectFilter, disabled: subjects.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "subject-filter", className: "w-full sm:w-[280px]" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("settingsModal.topics.filterPlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "all" }, t4("settingsModal.topics.allSubjects")), subjects.map((subject) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: subject, value: subject }, subject))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "border rounded-lg" }, /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-72" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, { className: "sticky top-0 bg-muted" }, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "w-[150px]" }, t4("settingsModal.topics.tableHeaders.subject")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "w-[150px]" }, t4("settingsModal.topics.tableHeaders.category")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, t4("settingsModal.topics.tableHeaders.topic")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "w-[100px]" }, t4("settingsModal.topics.tableHeaders.loId")))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { colSpan: 4, className: "text-center" }, t4("common.loading"))) : filteredLearningObjectives.length > 0 ? filteredLearningObjectives.map((lo) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: lo.loId }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, lo.subject), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, lo.category), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium" }, lo.topic), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, lo.loId))) : /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { colSpan: 4, className: "text-center h-24 text-muted-foreground" }, t4("settingsModal.topics.emptyData"))))))))), /* @__PURE__ */ React169__namespace.default.createElement(CardFooter, { className: "px-1" }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "destructive", disabled: learningObjectives.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "mr-2 h-4 w-4" }), t4("settingsModal.topics.clearAllData"))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, t4("settingsModal.topics.clearDataConfirmationTitle")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, t4("settingsModal.topics.clearDataConfirmationMessage", { count: learningObjectives.length }))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: handleClearData }, t4("settingsModal.topics.confirmDelete"))))))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isConfirmModalOpen, onOpenChange: setIsConfirmModalOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, t4("settingsModal.topics.confirmModal.title")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, t4("settingsModal.topics.confirmModal.description", { count: parsedImportData?.data.length || 0 }))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, { className: "gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { onClick: () => setParsedImportData(null) }, t4("common.cancel")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: handleConfirmMerge, className: "bg-blue-600 hover:bg-blue-700" }, t4("settingsModal.topics.confirmModal.mergeButton")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: handleConfirmOverwrite, className: "bg-amber-600 hover:bg-amber-700" }, t4("settingsModal.topics.confirmModal.overwriteButton"))))));
134778
+ } }, t4("settingsModal.topics.viewHeaderFormat"))), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "tsv-importer", type: "file", ref: fileInputRef, accept: ".tsv,text/tab-separated-values", onChange: handleFileChange, className: "hidden" }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: () => fileInputRef.current?.click() }, /* @__PURE__ */ React169__namespace.default.createElement(Upload, { className: "mr-2 h-4 w-4" }), t4("settingsModal.topics.chooseFile")))), importErrors.length > 0 && /* @__PURE__ */ React169__namespace.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleAlert, { className: "h-4 w-4" }), /* @__PURE__ */ React169__namespace.default.createElement(AlertTitle, null, t4("settingsModal.topics.importErrors")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDescription, null, /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-24 mt-2" }, /* @__PURE__ */ React169__namespace.default.createElement("ul", { className: "list-disc list-inside text-xs space-y-1" }, importErrors.map((error, index3) => /* @__PURE__ */ React169__namespace.default.createElement("li", { key: index3 }, error)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col sm:flex-row sm:items-center sm:justify-between mb-2" }, /* @__PURE__ */ React169__namespace.default.createElement("h3", { className: "text-lg font-semibold" }, t4("settingsModal.topics.currentDataTitle")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("settingsModal.topics.showingCount", { shown: filteredLearningObjectives.length, total: learningObjectives.length }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "mb-4" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subject-filter" }, t4("settingsModal.topics.filterBySubject")), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedSubjectFilter, onValueChange: setSelectedSubjectFilter, disabled: subjects.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "subject-filter", className: "w-full sm:w-[280px]" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("settingsModal.topics.filterPlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "all" }, t4("settingsModal.topics.allSubjects")), subjects.map((subject) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: subject, value: subject }, subject))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "border rounded-lg" }, /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-72" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, { className: "sticky top-0 bg-muted" }, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "w-[150px]" }, t4("settingsModal.topics.tableHeaders.subject")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "w-[150px]" }, t4("settingsModal.topics.tableHeaders.category")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, t4("settingsModal.topics.tableHeaders.topic")), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "w-[100px]" }, t4("settingsModal.topics.tableHeaders.code")))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { colSpan: 4, className: "text-center" }, t4("common.loading"))) : filteredLearningObjectives.length > 0 ? filteredLearningObjectives.map((lo) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: lo.code }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, lo.subject), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, lo.category), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, lo.topic), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, lo.code))) : /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { colSpan: 4, className: "text-center h-24 text-muted-foreground" }, t4("settingsModal.topics.emptyData"))))))))), /* @__PURE__ */ React169__namespace.default.createElement(CardFooter, { className: "px-1" }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "destructive", disabled: learningObjectives.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "mr-2 h-4 w-4" }), t4("settingsModal.topics.clearAllData"))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, t4("settingsModal.topics.clearDataConfirmationTitle")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, t4("settingsModal.topics.clearDataConfirmationMessage", { count: learningObjectives.length }))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: handleClearData }, t4("settingsModal.topics.confirmDelete"))))))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isConfirmModalOpen, onOpenChange: setIsConfirmModalOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, t4("settingsModal.topics.confirmModal.title")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, t4("settingsModal.topics.confirmModal.description", { count: parsedImportData?.data.length || 0 }))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, { className: "gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { onClick: () => setParsedImportData(null) }, t4("common.cancel")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: handleConfirmMerge, className: "bg-blue-600 hover:bg-blue-700" }, t4("settingsModal.topics.confirmModal.mergeButton")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: handleConfirmOverwrite, className: "bg-amber-600 hover:bg-amber-700" }, t4("settingsModal.topics.confirmModal.overwriteButton"))))));
134779
134779
  };
134780
134780
 
134781
134781
  // src/react-ui/components/app/ManageImageContexts.tsx
@@ -135182,7 +135182,7 @@ var SettingsModal = ({ isOpen, onClose, defaultTab = "personal" }) => {
135182
135182
  return goal.id;
135183
135183
  }
135184
135184
  };
135185
- return /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => !open && onClose() }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-xl md:max-w-2xl lg:max-w-3xl max-h-[85vh] flex flex-col" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, { className: "shrink-0" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Settings, { className: "mr-2 h-5 w-5 text-primary" }), t4("settingsModal.title")), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, t4("settingsModal.description"))), /* @__PURE__ */ React169__namespace.default.createElement(Tabs2, { value: activeTab, onValueChange: (value) => setActiveTab(value), className: "pt-2 flex-1 flex flex-col min-h-0" }, /* @__PURE__ */ React169__namespace.default.createElement(TabsList2, { className: "grid w-full grid-cols-5 shrink-0" }, /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "personal" }, /* @__PURE__ */ React169__namespace.default.createElement(User, { className: "mr-1 h-4 w-4" }), t4("settingsModal.personalTab")), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "topics" }, /* @__PURE__ */ React169__namespace.default.createElement(ListTodo, { className: "mr-1 h-4 w-4" }), t4("settingsModal.topicsTab")), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "imageContexts" }, /* @__PURE__ */ React169__namespace.default.createElement(ImagePlus, { className: "mr-1 h-4 w-4" }), "Images"), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "layout" }, /* @__PURE__ */ React169__namespace.default.createElement(LayoutDashboard, { className: "mr-1 h-4 w-4" }), t4("settingsModal.layoutTab")), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "apiKeys" }, /* @__PURE__ */ React169__namespace.default.createElement(KeyRound, { className: "mr-1 h-4 w-4" }), t4("settingsModal.apiKeysTab"))), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "personal", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-full pr-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold mb-3" }, t4("settingsModal.personal.basicInfo")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "full-name" }, t4("settingsModal.personal.fullName")), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "full-name", value: fullName, onChange: (e3) => setFullName(e3.target.value), placeholder: t4("settingsModal.personal.fullNamePlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "weekly-goal" }, t4("settingsModal.personal.weeklyGoal")), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "weekly-goal", type: "number", value: weeklyGoal, onChange: (e3) => setWeeklyGoal(parseInt(e3.target.value, 10) || 0), min: "1" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "language-select", className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Languages, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.languageSelectLabel")), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: language3, onValueChange: changeLanguage2 }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "language-select" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select a language..." })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "en" }, "English"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "vi" }, "Ti\u1EBFng Vi\u1EC7t")))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold mb-3 flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Target, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.advancedGoals")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 mb-4" }, advancedGoals.map((goal) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: goal.id, className: "flex items-center justify-between p-2 bg-muted/50 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm flex-1" }, renderGoalDescription(goal)), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleDeleteGoal(goal.id) }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), advancedGoals.length === 0 && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("settingsModal.personal.noGoals"))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement("h5", { className: "font-medium" }, t4("settingsModal.personal.addNewGoal")), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: newGoalType, onValueChange: (v) => setNewGoalType(v) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.goalTypePlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "average_score_subject" }, t4("settingsModal.personal.goalType.avgScoreSubject")), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "mastery_topic" }, t4("settingsModal.personal.goalType.masteryTopic")))), newGoalType === "average_score_subject" && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-2 animate-in fade-in" }, /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: newGoalSubject, onValueChange: setNewGoalSubject }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.subjectPlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, allSubjects.map((s4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: s4, value: s4 }, s4)))), /* @__PURE__ */ React169__namespace.default.createElement(Input, { type: "number", value: newGoalTargetValue, onChange: (e3) => setNewGoalTargetValue(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.targetScorePlaceholder") })), newGoalType === "mastery_topic" && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-2 animate-in fade-in" }, /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: newGoalSubject, onValueChange: setNewGoalSubject }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.subjectPlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, allSubjects.map((s4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: s4, value: s4 }, s4)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: newGoalTopic, onValueChange: setNewGoalTopic, disabled: !newGoalSubject }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.topicPlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, topicsForSelectedSubject.map((t5) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: t5, value: t5 }, t5)))), /* @__PURE__ */ React169__namespace.default.createElement(Input, { type: "number", value: newGoalTargetValue, onChange: (e3) => setNewGoalTargetValue(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.targetScorePlaceholder") }), /* @__PURE__ */ React169__namespace.default.createElement(Input, { type: "number", value: newGoalConsecutive, onChange: (e3) => setNewGoalConsecutive(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.consecutiveSessionsPlaceholder") })), newGoalType && /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", onClick: handleAddNewGoal }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.addGoalButton"))))))), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "topics", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__namespace.default.createElement(ManageTopics, null)), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "imageContexts", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__namespace.default.createElement(ManageImageContexts, null)), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "layout", className: "space-y-4 pt-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold" }, t4("settingsModal.layout.title")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground mt-1 mb-3" }, t4("settingsModal.layout.description")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(RefreshCw, { className: "mr-2 h-4 w-4" }), t4("settingsModal.layout.resetButton"))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, t4("settingsModal.layout.resetConfirmationTitle")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, t4("settingsModal.layout.resetConfirmationMessage"))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: handleResetLayout }, t4("settingsModal.layout.confirmReset"))))))), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "apiKeys", className: "space-y-4 pt-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "gemini-api-key" }, t4("settingsModal.apiKeys.geminiKey")), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "gemini-api-key", type: "password", value: geminiApiKey, onChange: (e3) => setGeminiApiKey(e3.target.value), placeholder: t4("settingsModal.apiKeys.geminiKeyPlaceholder") }), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-muted-foreground" }, t4("settingsModal.apiKeys.storageHint"))))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, { className: "gap-2 sm:justify-end pt-4 shrink-0" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline" }, t4("common.close"))), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", onClick: handleSave }, /* @__PURE__ */ React169__namespace.default.createElement(Save, { className: "mr-2 h-4 w-4" }), t4("settingsModal.saveChanges")))));
135185
+ return /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => !open && onClose() }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-xl md:max-w-2xl lg:max-w-3xl max-h-[85vh] flex flex-col" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, { className: "shrink-0" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Settings, { className: "mr-2 h-5 w-5 text-primary" }), t4("settingsModal.title")), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, t4("settingsModal.description"))), /* @__PURE__ */ React169__namespace.default.createElement(Tabs2, { value: activeTab, onValueChange: (value) => setActiveTab(value), className: "pt-2 flex-1 flex flex-col min-h-0" }, /* @__PURE__ */ React169__namespace.default.createElement(TabsList2, { className: "grid w-full grid-cols-5 shrink-0" }, /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "personal" }, /* @__PURE__ */ React169__namespace.default.createElement(User, { className: "mr-1 h-4 w-4" }), t4("settingsModal.personalTab")), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "topics" }, /* @__PURE__ */ React169__namespace.default.createElement(ListTodo, { className: "mr-1 h-4 w-4" }), t4("settingsModal.topicsTab")), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "imageContexts" }, /* @__PURE__ */ React169__namespace.default.createElement(ImagePlus, { className: "mr-1 h-4 w-4" }), "Images"), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "layout" }, /* @__PURE__ */ React169__namespace.default.createElement(LayoutDashboard, { className: "mr-1 h-4 w-4" }), t4("settingsModal.layoutTab")), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "apiKeys" }, /* @__PURE__ */ React169__namespace.default.createElement(KeyRound, { className: "mr-1 h-4 w-4" }), t4("settingsModal.apiKeysTab"))), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "personal", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-full pr-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold mb-3" }, t4("settingsModal.personal.basicInfo")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "full-name" }, t4("settingsModal.personal.fullName")), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "full-name", value: fullName, onChange: (e3) => setFullName(e3.target.value), placeholder: t4("settingsModal.personal.fullNamePlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "weekly-goal" }, t4("settingsModal.personal.weeklyGoal")), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "weekly-goal", type: "number", value: weeklyGoal, onChange: (e3) => setWeeklyGoal(parseInt(e3.target.value, 10) || 0), min: "1" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "language-select", className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Languages, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.languageSelectLabel")), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: language3, onValueChange: changeLanguage2 }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "language-select" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select a language..." })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "en" }, "English"), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "vi" }, "Ti\u1EBFng Vi\u1EC7t")))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold mb-3 flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Target, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.advancedGoals")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2 mb-4" }, advancedGoals.map((goal) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: goal.id, className: "flex items-center justify-between p-2 bg-muted/50 rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm flex-1" }, renderGoalDescription(goal)), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => handleDeleteGoal(goal.id) }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4 text-destructive" })))), advancedGoals.length === 0 && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, t4("settingsModal.personal.noGoals"))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3 pt-4 border-t" }, /* @__PURE__ */ React169__namespace.default.createElement("h5", { className: "font-Medium" }, t4("settingsModal.personal.addNewGoal")), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: newGoalType, onValueChange: (v) => setNewGoalType(v) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.goalTypePlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "average_score_subject" }, t4("settingsModal.personal.goalType.avgScoreSubject")), /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "mastery_topic" }, t4("settingsModal.personal.goalType.masteryTopic")))), newGoalType === "average_score_subject" && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-2 animate-in fade-in" }, /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: newGoalSubject, onValueChange: setNewGoalSubject }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.subjectPlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, allSubjects.map((s4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: s4, value: s4 }, s4)))), /* @__PURE__ */ React169__namespace.default.createElement(Input, { type: "number", value: newGoalTargetValue, onChange: (e3) => setNewGoalTargetValue(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.targetScorePlaceholder") })), newGoalType === "mastery_topic" && /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-2 gap-2 animate-in fade-in" }, /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: newGoalSubject, onValueChange: setNewGoalSubject }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.subjectPlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, allSubjects.map((s4) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: s4, value: s4 }, s4)))), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: newGoalTopic, onValueChange: setNewGoalTopic, disabled: !newGoalSubject }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: t4("settingsModal.personal.topicPlaceholder") })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, topicsForSelectedSubject.map((t5) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: t5, value: t5 }, t5)))), /* @__PURE__ */ React169__namespace.default.createElement(Input, { type: "number", value: newGoalTargetValue, onChange: (e3) => setNewGoalTargetValue(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.targetScorePlaceholder") }), /* @__PURE__ */ React169__namespace.default.createElement(Input, { type: "number", value: newGoalConsecutive, onChange: (e3) => setNewGoalConsecutive(parseInt(e3.target.value)), placeholder: t4("settingsModal.personal.consecutiveSessionsPlaceholder") })), newGoalType && /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", onClick: handleAddNewGoal }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), t4("settingsModal.personal.addGoalButton"))))))), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "topics", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__namespace.default.createElement(ManageTopics, null)), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "imageContexts", className: "flex-1 overflow-auto mt-4" }, /* @__PURE__ */ React169__namespace.default.createElement(ManageImageContexts, null)), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "layout", className: "space-y-4 pt-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 border rounded-lg" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold" }, t4("settingsModal.layout.title")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground mt-1 mb-3" }, t4("settingsModal.layout.description")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(RefreshCw, { className: "mr-2 h-4 w-4" }), t4("settingsModal.layout.resetButton"))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, t4("settingsModal.layout.resetConfirmationTitle")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, t4("settingsModal.layout.resetConfirmationMessage"))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, null, t4("common.cancel")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: handleResetLayout }, t4("settingsModal.layout.confirmReset"))))))), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "apiKeys", className: "space-y-4 pt-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "gemini-api-key" }, t4("settingsModal.apiKeys.geminiKey")), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "gemini-api-key", type: "password", value: geminiApiKey, onChange: (e3) => setGeminiApiKey(e3.target.value), placeholder: t4("settingsModal.apiKeys.geminiKeyPlaceholder") }), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-muted-foreground" }, t4("settingsModal.apiKeys.storageHint"))))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, { className: "gap-2 sm:justify-end pt-4 shrink-0" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline" }, t4("common.close"))), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", onClick: handleSave }, /* @__PURE__ */ React169__namespace.default.createElement(Save, { className: "mr-2 h-4 w-4" }), t4("settingsModal.saveChanges")))));
135186
135186
  };
135187
135187
 
135188
135188
  // src/react-ui/components/dashboard/Cheatsheet.tsx
@@ -135281,7 +135281,7 @@ var Cheatsheet = () => {
135281
135281
  }
135282
135282
  return null;
135283
135283
  };
135284
- return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), t4("knowledgeCards.title")), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, null, t4("knowledgeCards.description"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React169__namespace.default.createElement(Input, { type: "search", placeholder: t4("knowledgeCards.searchPlaceholder"), value: searchQuery, onChange: (e3) => setSearchQuery2(e3.target.value), disabled: allCards.length === 0 }), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, allCards.length > 0 ? filteredCards.length > 0 ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, filteredCards.map((card) => /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("knowledgeCards.noMatch"))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("knowledgeCards.empty")))), renderActionArea())), /* @__PURE__ */ React169__namespace.default.createElement(
135284
+ return /* @__PURE__ */ React169__namespace.default.createElement(React169__namespace.default.Fragment, null, /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), t4("knowledgeCards.title")), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, null, t4("knowledgeCards.description"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "space-y-4" }, /* @__PURE__ */ React169__namespace.default.createElement(Input, { type: "search", placeholder: t4("knowledgeCards.searchPlaceholder"), value: searchQuery, onChange: (e3) => setSearchQuery2(e3.target.value), disabled: allCards.length === 0 }), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, allCards.length > 0 ? filteredCards.length > 0 ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, filteredCards.map((card) => /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("knowledgeCards.noMatch"))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("knowledgeCards.empty")))), renderActionArea())), /* @__PURE__ */ React169__namespace.default.createElement(
135285
135285
  CardViewerDialog,
135286
135286
  {
135287
135287
  isOpen: isDialogOpen,
@@ -135308,9 +135308,9 @@ var StatCard = ({
135308
135308
  isLoading = false
135309
135309
  }) => {
135310
135310
  if (isLoading) {
135311
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-sm font-medium" }, title), /* @__PURE__ */ React169__namespace.default.createElement(Skeleton, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement(Skeleton, { className: "h-8 w-3/4 mb-2" }), /* @__PURE__ */ React169__namespace.default.createElement(Skeleton, { className: "h-4 w-1/2" })));
135311
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-sm font-Medium" }, title), /* @__PURE__ */ React169__namespace.default.createElement(Skeleton, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement(Skeleton, { className: "h-8 w-3/4 mb-2" }), /* @__PURE__ */ React169__namespace.default.createElement(Skeleton, { className: "h-4 w-1/2" })));
135312
135312
  }
135313
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-sm font-medium" }, title), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "text-muted-foreground" }, icon)), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "text-2xl font-bold" }, value, unit2 && /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-xl font-medium text-muted-foreground ml-1" }, unit2)), context && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-muted-foreground" }, context)));
135313
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 pb-2" }, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "text-sm font-Medium" }, title), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "text-muted-foreground" }, icon)), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "text-2xl font-bold" }, value, unit2 && /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "text-xl font-Medium text-muted-foreground ml-1" }, unit2)), context && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs text-muted-foreground" }, context)));
135314
135314
  };
135315
135315
 
135316
135316
  // src/react-ui/components/dashboard/PerformanceSnapshot.tsx
@@ -135409,7 +135409,7 @@ var RoadmapChecklist = () => {
135409
135409
  }, []);
135410
135410
  const handleStartPractice = React169.useCallback((item) => {
135411
135411
  const practiceConfig = {
135412
- loIds: [item.loId],
135412
+ loIds: [item.code],
135413
135413
  difficulty: item.suggestedDifficulty,
135414
135414
  language: "Vietnamese"
135415
135415
  };
@@ -135419,7 +135419,7 @@ var RoadmapChecklist = () => {
135419
135419
  return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(CalendarCheck, { className: "mr-2 h-5 w-5 text-primary" }), t4("roadmap.title")), /* @__PURE__ */ React169__namespace.default.createElement(CardDescription, null, t4("roadmap.description"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, !roadmap || !roadmap.items || roadmap.items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "text-center text-muted-foreground py-8" }, /* @__PURE__ */ React169__namespace.default.createElement("p", null, t4("roadmap.emptyState")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm" }, t4("roadmap.emptyStateSuggestion"))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, roadmap.items.map((item, index3) => /* @__PURE__ */ React169__namespace.default.createElement(
135420
135420
  "div",
135421
135421
  {
135422
- key: `${item.loId}-${index3}`,
135422
+ key: `${item.code}-${index3}`,
135423
135423
  className: "flex items-center justify-between p-3 border rounded-md bg-background hover:bg-muted/50 transition-colors"
135424
135424
  },
135425
135425
  /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-start gap-3" }, /* @__PURE__ */ React169__namespace.default.createElement(
@@ -136962,7 +136962,7 @@ function Calendar2({
136962
136962
  months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
136963
136963
  month: "space-y-4",
136964
136964
  caption: "flex justify-center pt-1 relative items-center",
136965
- caption_label: "text-sm font-medium",
136965
+ caption_label: "text-sm font-Medium",
136966
136966
  nav: "space-x-1 flex items-center",
136967
136967
  nav_button: cn(
136968
136968
  buttonVariants({ variant: "outline" }),
@@ -137084,7 +137084,7 @@ var AnalysisDialog = ({ isOpen, onClose }) => {
137084
137084
  }
137085
137085
  try {
137086
137086
  const allAvailableTopics = TopicDataService.getData().map((lo) => ({
137087
- loId: lo.loId,
137087
+ code: lo.code,
137088
137088
  subject: lo.subject,
137089
137089
  category: lo.category,
137090
137090
  topic: lo.topic
@@ -137537,7 +137537,7 @@ var GeneratedQuizzesCard = () => {
137537
137537
  onChange: (e3) => setSearchQuery2(e3.target.value),
137538
137538
  disabled: uniqueQuizzes.length === 0
137539
137539
  }
137540
- ), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, uniqueQuizzes.length > 0 ? filteredQuizzes.length > 0 ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, filteredQuizzes.map((quiz) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: quiz.id, className: "flex items-center justify-between p-2 rounded-md hover:bg-accent" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm font-medium truncate pr-2", title: quiz.title }, quiz.title), /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", variant: "ghost", onClick: () => handleRetake(quiz) }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlay, { className: "mr-2 h-4 w-4" }), t4("common.retake"))))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.generated.noMatch"))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.generated.empty"))))));
137540
+ ), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, uniqueQuizzes.length > 0 ? filteredQuizzes.length > 0 ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, filteredQuizzes.map((quiz) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: quiz.id, className: "flex items-center justify-between p-2 rounded-md hover:bg-accent" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm font-Medium truncate pr-2", title: quiz.title }, quiz.title), /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", variant: "ghost", onClick: () => handleRetake(quiz) }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlay, { className: "mr-2 h-4 w-4" }), t4("common.retake"))))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.generated.noMatch"))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.generated.empty"))))));
137541
137541
  };
137542
137542
 
137543
137543
  // src/react-ui/components/app/UploadResourceModal.tsx
@@ -137617,11 +137617,12 @@ var UploadResourceModal = ({ isOpen, onClose }) => {
137617
137617
  language: i18n.language === "vi" ? "Vietnamese" : "English",
137618
137618
  documentContent: textContent,
137619
137619
  learningObjectives: learningObjectives.map((lo) => ({
137620
- loId: lo.loId,
137620
+ name: lo.name,
137621
+ code: lo.code,
137621
137622
  subject: lo.subject,
137622
137623
  category: lo.category,
137623
137624
  topic: lo.topic,
137624
- loDescription: lo.loDescription
137625
+ description: lo.description || ""
137625
137626
  }))
137626
137627
  }, apiKey);
137627
137628
  setAnalysisResult(result);
@@ -137654,12 +137655,12 @@ var UploadResourceModal = ({ isOpen, onClose }) => {
137654
137655
  generatedQuestions = result.generatedQuestions;
137655
137656
  } else {
137656
137657
  const plan = analysisResult.mappedLOs.map((lo) => {
137657
- const sourceLO = TopicDataService.getData().find((orig) => orig.loId === lo.loId);
137658
+ const sourceLO = TopicDataService.getData().find((orig) => orig.code === lo.code);
137658
137659
  return {
137659
137660
  plannedTopic: lo.reasoning,
137660
137661
  plannedQuestionType: "multiple_choice",
137661
137662
  plannedBloomLevel: "understanding",
137662
- originalLoId: lo.loId,
137663
+ originalLoId: lo.code,
137663
137664
  originalTopic: sourceLO?.topic,
137664
137665
  originalCategory: sourceLO?.category,
137665
137666
  originalSubject: sourceLO?.subject
@@ -137704,7 +137705,7 @@ var UploadResourceModal = ({ isOpen, onClose }) => {
137704
137705
  return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col items-center justify-center h-48" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-12 w-12 animate-spin text-primary" }), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "mt-4 text-muted-foreground font-semibold" }, stage === "analyzing" && t4("dialogs.uploadResource.analyzing"), stage === "generating" && t4("dialogs.uploadResource.generating")));
137705
137706
  case "result":
137706
137707
  if (!analysisResult) return null;
137707
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "py-4 space-y-4" }, /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "p-4" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold mb-2" }, "AI Analysis Complete"), analysisResult.isFreestyleRecommended ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-start gap-3 text-amber-700" }, /* @__PURE__ */ React169__namespace.default.createElement(BrainCircuit, { className: "h-5 w-5 mt-1 flex-shrink-0" }), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-medium" }, t4("dialogs.uploadResource.freestyleRecommended")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm" }, t4("dialogs.uploadResource.freestyleDescription")))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-start gap-3 text-green-700" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleCheckBig, { className: "h-5 w-5 mt-1 flex-shrink-0" }), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-medium" }, t4("dialogs.uploadResource.curriculumMatch")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm" }, t4("dialogs.uploadResource.curriculumDescription")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs mt-2 font-semibold" }, t4("dialogs.uploadResource.mappedTopics")), /* @__PURE__ */ React169__namespace.default.createElement("ul", { className: "list-disc list-inside text-xs" }, analysisResult.mappedLOs.map((lo) => /* @__PURE__ */ React169__namespace.default.createElement("li", { key: lo.loId }, TopicDataService.getData().find((orig) => orig.loId === lo.loId)?.topic || lo.loId))))))), error && /* @__PURE__ */ React169__namespace.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleAlert, { className: "h-4 w-4" }), /* @__PURE__ */ React169__namespace.default.createElement(AlertTitle, null, t4("common.error")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDescription, null, error)));
137708
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "py-4 space-y-4" }, /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardContent, { className: "p-4" }, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold mb-2" }, "AI Analysis Complete"), analysisResult.isFreestyleRecommended ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-start gap-3 text-amber-700" }, /* @__PURE__ */ React169__namespace.default.createElement(BrainCircuit, { className: "h-5 w-5 mt-1 flex-shrink-0" }), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-Medium" }, t4("dialogs.uploadResource.freestyleRecommended")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm" }, t4("dialogs.uploadResource.freestyleDescription")))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-start gap-3 text-green-700" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleCheckBig, { className: "h-5 w-5 mt-1 flex-shrink-0" }), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-Medium" }, t4("dialogs.uploadResource.curriculumMatch")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm" }, t4("dialogs.uploadResource.curriculumDescription")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-xs mt-2 font-semibold" }, t4("dialogs.uploadResource.mappedTopics")), /* @__PURE__ */ React169__namespace.default.createElement("ul", { className: "list-disc list-inside text-xs" }, analysisResult.mappedLOs.map((lo) => /* @__PURE__ */ React169__namespace.default.createElement("li", { key: lo.code }, TopicDataService.getData().find((orig) => orig.code === lo.code)?.topic || lo.code))))))), error && /* @__PURE__ */ React169__namespace.default.createElement(Alert, { variant: "destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleAlert, { className: "h-4 w-4" }), /* @__PURE__ */ React169__namespace.default.createElement(AlertTitle, null, t4("common.error")), /* @__PURE__ */ React169__namespace.default.createElement(AlertDescription, null, error)));
137708
137709
  }
137709
137710
  };
137710
137711
  return /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => !open && onClose() }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-lg" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(FileText, { className: "mr-2 h-5 w-5" }), t4("dialogs.uploadResource.title")), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, t4("dialogs.uploadResource.description"))), renderContent3(), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "outline" }, t4("common.cancel"))), stage === "result" && /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleGenerateQuiz }, /* @__PURE__ */ React169__namespace.default.createElement(Sparkles, { className: "mr-2 h-4 w-4" }), t4("dialogs.uploadResource.generateButton")))));
@@ -137825,7 +137826,7 @@ var FreestyleQuizzesCard = () => {
137825
137826
  onChange: (e3) => setSearchQuery2(e3.target.value),
137826
137827
  disabled: uniqueQuizzes.length === 0
137827
137828
  }
137828
- ), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, uniqueQuizzes.length > 0 ? filteredQuizzes.length > 0 ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, filteredQuizzes.map((quiz) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: quiz.id, className: "flex items-center justify-between p-2 rounded-md hover:bg-accent" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm font-medium truncate pr-2", title: quiz.title }, quiz.title), /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", variant: "ghost", onClick: () => handleRetake(quiz) }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlay, { className: "mr-2 h-4 w-4" }), t4("common.retake"))))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.freestyle.noMatch"))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.freestyle.empty"))))));
137829
+ ), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "h-[180px] w-full rounded-md border p-2" }, uniqueQuizzes.length > 0 ? filteredQuizzes.length > 0 ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-1" }, filteredQuizzes.map((quiz) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: quiz.id, className: "flex items-center justify-between p-2 rounded-md hover:bg-accent" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm font-Medium truncate pr-2", title: quiz.title }, quiz.title), /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", variant: "ghost", onClick: () => handleRetake(quiz) }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlay, { className: "mr-2 h-4 w-4" }), t4("common.retake"))))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.freestyle.noMatch"))) : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center justify-center h-full" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-muted-foreground" }, t4("quizLists.freestyle.empty"))))));
137829
137830
  };
137830
137831
 
137831
137832
  // src/react-ui/components/common/ClientTranslation.tsx
@@ -137975,7 +137976,7 @@ var PersonalPracticeDashboard = ({ settingsPath, initialHistory, initialStats, i
137975
137976
  }
137976
137977
  try {
137977
137978
  const allAvailableTopics = TopicDataService.getData().map((lo) => ({
137978
- loId: lo.loId,
137979
+ code: lo.code,
137979
137980
  subject: lo.subject,
137980
137981
  category: lo.category,
137981
137982
  topic: lo.topic
@@ -138469,7 +138470,7 @@ var PracticeModeController = () => {
138469
138470
  try {
138470
138471
  const config3 = JSON.parse(suggestedConfigString);
138471
138472
  const allLOs = TopicDataService.getData();
138472
- const suggestedLOs = allLOs.filter((lo) => config3.loIds?.includes(lo.loId));
138473
+ const suggestedLOs = allLOs.filter((lo) => config3.loIds?.includes(lo.code));
138473
138474
  if (suggestedLOs.length > 0) {
138474
138475
  setInitialSuggestedLOs(suggestedLOs);
138475
138476
  setInitialSuggestedDifficulty(config3.difficulty || "Medium");
@@ -138512,9 +138513,10 @@ var PracticeModeController = () => {
138512
138513
  totalQuestions,
138513
138514
  numCodingQuestions,
138514
138515
  topics: selectedLOs.map((lo) => ({
138515
- topic: lo.loDescription,
138516
+ topic: lo.name || lo.code,
138517
+ // FIX: Provide fallback for name
138516
138518
  ratio: 100 / selectedLOs.length,
138517
- originalLoId: lo.loId,
138519
+ originalLoId: lo.code,
138518
138520
  originalSubject: lo.subject,
138519
138521
  originalCategory: lo.category,
138520
138522
  originalTopic: lo.topic
@@ -138706,7 +138708,7 @@ var SuggestionDialog = ({
138706
138708
  if (!suggestion) {
138707
138709
  return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex flex-col items-center justify-center h-64 gap-4" }, /* @__PURE__ */ React169__namespace.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."));
138708
138710
  }
138709
- return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 bg-muted/50 rounded-lg" }, /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: suggestion.suggestionText })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold mb-3" }, "K\u1EBF ho\u1EA1ch Luy\u1EC7n t\u1EADp \u0111\u01B0\u1EE3c G\u1EE3i \xFD:"), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "max-h-[200px] pr-3" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, suggestion.suggestedTopics.map((topic) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: topic.loId, className: "flex items-center justify-between p-3 border rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex-1 mr-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, topic.reason === "review" ? /* @__PURE__ */ React169__namespace.default.createElement(Badge2, { variant: "destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(RefreshCw, { className: "h-3 w-3 mr-1.5" }), "\xD4n t\u1EADp") : /* @__PURE__ */ React169__namespace.default.createElement(Badge2, { variant: "secondary" }, /* @__PURE__ */ React169__namespace.default.createElement(Search, { className: "h-3 w-3 mr-1.5" }), "Kh\xE1m ph\xE1")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-medium" }, topic.topicName)), /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", onClick: () => onStartSuggestedPractice(topic) }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlay, { className: "h-4 w-4 mr-2" }), "B\u1EAFt \u0111\u1EA7u")))))));
138711
+ return /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-6" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "p-4 bg-muted/50 rounded-lg" }, /* @__PURE__ */ React169__namespace.default.createElement(MarkdownRenderer, { content: suggestion.suggestionText })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement("h4", { className: "font-semibold mb-3" }, "K\u1EBF ho\u1EA1ch Luy\u1EC7n t\u1EADp \u0111\u01B0\u1EE3c G\u1EE3i \xFD:"), /* @__PURE__ */ React169__namespace.default.createElement(ScrollArea2, { className: "max-h-[200px] pr-3" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, suggestion.suggestedTopics.map((topic) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: topic.code, className: "flex items-center justify-between p-3 border rounded-md" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex-1 mr-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2 mb-1" }, topic.reason === "review" ? /* @__PURE__ */ React169__namespace.default.createElement(Badge2, { variant: "destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(RefreshCw, { className: "h-3 w-3 mr-1.5" }), "\xD4n t\u1EADp") : /* @__PURE__ */ React169__namespace.default.createElement(Badge2, { variant: "secondary" }, /* @__PURE__ */ React169__namespace.default.createElement(Search, { className: "h-3 w-3 mr-1.5" }), "Kh\xE1m ph\xE1")), /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "font-Medium" }, topic.topicName)), /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", onClick: () => onStartSuggestedPractice(topic) }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlay, { className: "h-4 w-4 mr-2" }), "B\u1EAFt \u0111\u1EA7u")))))));
138710
138712
  };
138711
138713
  return /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: (open) => !open && onClose() }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-lg md:max-w-xl" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, { className: "flex items-center text-2xl" }, /* @__PURE__ */ React169__namespace.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__namespace.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__namespace.default.createElement("div", { className: "py-4" }, renderContent3()), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogClose2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline" }, "\u0110\xF3ng")))));
138712
138714
  };
@@ -138716,12 +138718,96 @@ init_react_shim();
138716
138718
 
138717
138719
  // src/react-ui/components/metadata/SubjectManager.tsx
138718
138720
  init_react_shim();
138721
+
138722
+ // src/react-ui/components/metadata/MetadataImportControls.tsx
138723
+ init_react_shim();
138724
+ function MetadataImportControls({ metadataName, onImport }) {
138725
+ const [isOpen, setIsOpen] = React169.useState(false);
138726
+ const [jsonString, setJsonString] = React169.useState("");
138727
+ const [isImporting, startImportTransition] = React169.useTransition();
138728
+ const fileInputRef = React169.useRef(null);
138729
+ const { toast: toast2 } = useToast();
138730
+ const processAndImportRecords = async (records, importSource) => {
138731
+ if (records.length === 0) {
138732
+ toast2({ title: "No Data", description: `The selected ${importSource === "file" ? "file" : "JSON string"} contains no data to import.`, variant: "destructive" });
138733
+ return;
138734
+ }
138735
+ await onImport(records);
138736
+ setIsOpen(false);
138737
+ setJsonString("");
138738
+ };
138739
+ const handleFileSelected = (event) => {
138740
+ const file = event.target.files?.[0];
138741
+ if (!file) return;
138742
+ if (file.type !== "application/json" && !file.name.endsWith(".csv")) {
138743
+ toast2({ title: "Invalid File Type", description: "Please select a JSON or CSV file.", variant: "destructive" });
138744
+ if (event.target) event.target.value = "";
138745
+ return;
138746
+ }
138747
+ startImportTransition(async () => {
138748
+ try {
138749
+ const fileContent = await file.text();
138750
+ let records = [];
138751
+ if (file.type === "application/json") {
138752
+ records = JSON.parse(fileContent);
138753
+ if (!Array.isArray(records)) throw new Error("JSON file must contain an array of objects.");
138754
+ } else if (file.name.endsWith(".csv")) {
138755
+ const lines = fileContent.split(/\r\n|\n/).filter((line) => line.trim() !== "");
138756
+ if (lines.length < 2) throw new Error("CSV must have a header and at least one data row.");
138757
+ const headers = lines[0].split(",").map((h3) => h3.trim());
138758
+ records = lines.slice(1).map((line) => {
138759
+ const values = line.split(",").map((v) => v.trim());
138760
+ const record = {};
138761
+ headers.forEach((header, index3) => {
138762
+ record[header] = values[index3];
138763
+ });
138764
+ return record;
138765
+ });
138766
+ }
138767
+ await processAndImportRecords(records, "file");
138768
+ } catch (err) {
138769
+ toast2({ title: "Import Error", description: `Failed to process file: ${err.message}`, variant: "destructive" });
138770
+ }
138771
+ });
138772
+ if (event.target) event.target.value = "";
138773
+ };
138774
+ const handleJsonStringImport = () => {
138775
+ if (!jsonString.trim()) {
138776
+ toast2({ title: "Missing Data", description: "JSON string cannot be empty.", variant: "destructive" });
138777
+ return;
138778
+ }
138779
+ startImportTransition(async () => {
138780
+ try {
138781
+ const records = JSON.parse(jsonString);
138782
+ if (!Array.isArray(records)) throw new Error("JSON string must represent an array of objects.");
138783
+ await processAndImportRecords(records, "text");
138784
+ } catch (err) {
138785
+ toast2({ title: "Import Error", description: `Failed to process JSON string: ${err.message}`, variant: "destructive" });
138786
+ }
138787
+ });
138788
+ };
138789
+ return /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", variant: "outline" }, /* @__PURE__ */ React169__namespace.default.createElement(Upload, { className: "mr-2 h-4 w-4" }), " Import ", metadataName)), /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, "Bulk Import ", metadataName), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, "Import multiple records from a file or by pasting JSON data.")), /* @__PURE__ */ React169__namespace.default.createElement(Tabs2, { defaultValue: "file" }, /* @__PURE__ */ React169__namespace.default.createElement(TabsList2, { className: "grid w-full grid-cols-2" }, /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "file" }, /* @__PURE__ */ React169__namespace.default.createElement(FileJson, { className: "mr-2 h-4 w-4" }), " From File"), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "text" }, /* @__PURE__ */ React169__namespace.default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), " From Text")), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "file", className: "pt-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(Button, { variant: "outline", onClick: () => fileInputRef.current?.click(), disabled: isImporting }, isImporting ? /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__namespace.default.createElement(Upload, { className: "mr-2 h-4 w-4" }), isImporting ? "Importing..." : "Select File"), /* @__PURE__ */ React169__namespace.default.createElement("input", { type: "file", ref: fileInputRef, onChange: handleFileSelected, accept: ".json,.csv", className: "hidden" }))), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "text", className: "pt-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "jsonInput" }, "JSON Data (Array of Objects)"), /* @__PURE__ */ React169__namespace.default.createElement(
138790
+ Textarea,
138791
+ {
138792
+ id: "jsonInput",
138793
+ value: jsonString,
138794
+ onChange: (e3) => setJsonString(e3.target.value),
138795
+ placeholder: '[{"code": "SUB1", "name": "Subject 1"}, ...]',
138796
+ rows: 8,
138797
+ className: "font-mono text-xs",
138798
+ disabled: isImporting
138799
+ }
138800
+ ), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleJsonStringImport, disabled: isImporting || !jsonString.trim() }, isImporting ? /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__namespace.default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), "Import from Text"))))));
138801
+ }
138802
+
138803
+ // src/react-ui/components/metadata/SubjectManager.tsx
138719
138804
  function SubjectManager({
138720
138805
  initialData,
138721
138806
  isLoading: isLoadingProp,
138722
138807
  onAdd,
138723
138808
  onUpdate,
138724
- onDelete
138809
+ onDelete,
138810
+ onBulkAdd
138725
138811
  }) {
138726
138812
  const [subjects, setSubjects] = React169.useState([]);
138727
138813
  const [isLoading, setIsLoading] = React169.useState(true);
@@ -138819,12 +138905,37 @@ function SubjectManager({
138819
138905
  }
138820
138906
  });
138821
138907
  };
138822
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "subjectCode", value: subjectCode, onChange: (e3) => setSubjectCode(e3.target.value.toUpperCase()), placeholder: "e.g., MATH", disabled: !!currentSubject })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "subjectName", value: subjectName, onChange: (e3) => setSubjectName(e3.target.value), placeholder: "e.g., Mathematics" }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
138908
+ const handleImport = async (records) => {
138909
+ if (!onBulkAdd) return;
138910
+ const validatedRecords = records.map((rec) => {
138911
+ if (typeof rec.code === "string" && typeof rec.name === "string") {
138912
+ return { code: rec.code, name: rec.name };
138913
+ }
138914
+ return null;
138915
+ }).filter((rec) => rec !== null);
138916
+ if (validatedRecords.length !== records.length) {
138917
+ toast2({
138918
+ title: "Import Warning",
138919
+ description: "Some records had invalid or missing 'code' or 'name' fields and were ignored.",
138920
+ variant: "destructive"
138921
+ });
138922
+ }
138923
+ if (validatedRecords.length > 0) {
138924
+ await onBulkAdd(validatedRecords);
138925
+ }
138926
+ };
138927
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(
138928
+ MetadataImportControls,
138929
+ {
138930
+ metadataName: "Subjects",
138931
+ onImport: handleImport
138932
+ }
138933
+ ), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject")))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, subject.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "subjectCode", value: subjectCode, onChange: (e3) => setSubjectCode(e3.target.value.toUpperCase()), placeholder: "e.g., MATH", disabled: !!currentSubject })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "subjectName", value: subjectName, onChange: (e3) => setSubjectName(e3.target.value), placeholder: "e.g., Mathematics" }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
138823
138934
  }
138824
138935
 
138825
138936
  // src/react-ui/components/metadata/GradeLevelManager.tsx
138826
138937
  init_react_shim();
138827
- function GradeLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
138938
+ function GradeLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
138828
138939
  const [items, setItems] = React169.useState([]);
138829
138940
  const [isLoading, setIsLoading] = React169.useState(true);
138830
138941
  const [isDialogOpen, setIsDialogOpen] = React169.useState(false);
@@ -138921,7 +139032,26 @@ function GradeLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpd
138921
139032
  }
138922
139033
  });
138923
139034
  };
138924
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Award, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Grade Levels"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Grade Level"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No grade levels found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Grade Level" : "Add New Grade Level")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., G9", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Grade 9" }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139035
+ const handleImport = async (records) => {
139036
+ if (!onBulkAdd) return;
139037
+ const validatedRecords = records.map((rec) => {
139038
+ if (typeof rec.code === "string" && typeof rec.name === "string") {
139039
+ return { code: rec.code, name: rec.name };
139040
+ }
139041
+ return null;
139042
+ }).filter((rec) => rec !== null);
139043
+ if (validatedRecords.length !== records.length) {
139044
+ toast2({
139045
+ title: "Import Warning",
139046
+ description: "Some records had invalid or missing 'code' or 'name' fields and were ignored.",
139047
+ variant: "destructive"
139048
+ });
139049
+ }
139050
+ if (validatedRecords.length > 0) {
139051
+ await onBulkAdd(validatedRecords);
139052
+ }
139053
+ };
139054
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Award, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Grade Levels"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(MetadataImportControls, { metadataName: "Grade Levels", onImport: handleImport }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Grade Level")))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No grade levels found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Grade Level" : "Add New Grade Level")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., G9", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Grade 9" }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
138925
139055
  }
138926
139056
 
138927
139057
  // src/react-ui/components/metadata/TopicManager.tsx
@@ -138932,7 +139062,8 @@ function TopicManager({
138932
139062
  isLoading: isLoadingProp,
138933
139063
  onAdd,
138934
139064
  onUpdate,
138935
- onDelete
139065
+ onDelete,
139066
+ onBulkAdd
138936
139067
  }) {
138937
139068
  const [topics, setTopics] = React169.useState([]);
138938
139069
  const [subjects, setSubjects] = React169.useState([]);
@@ -139036,15 +139167,34 @@ function TopicManager({
139036
139167
  }
139037
139168
  });
139038
139169
  };
139170
+ const handleImport = async (records) => {
139171
+ if (!onBulkAdd) return;
139172
+ const validatedRecords = records.map((rec) => {
139173
+ if (typeof rec.code === "string" && typeof rec.name === "string" && typeof rec.subjectCode === "string") {
139174
+ return { code: rec.code, name: rec.name, subjectCode: rec.subjectCode };
139175
+ }
139176
+ return null;
139177
+ }).filter((rec) => rec !== null);
139178
+ if (validatedRecords.length !== records.length) {
139179
+ toast2({
139180
+ title: "Import Warning",
139181
+ description: "Some records had invalid or missing 'code', 'name', or 'subjectCode' fields and were ignored.",
139182
+ variant: "destructive"
139183
+ });
139184
+ }
139185
+ if (validatedRecords.length > 0) {
139186
+ await onBulkAdd(validatedRecords);
139187
+ }
139188
+ };
139039
139189
  const getSubjectName = (subjectCode) => {
139040
139190
  return subjects.find((s4) => s4.code === subjectCode)?.name || "N/A";
139041
139191
  };
139042
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Tag, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Topics"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm", disabled: subjects.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic")), subjects.length === 0 && !isLoading && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-destructive" }, "Please add subjects before adding topics.")), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : topics.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No topics found. Add one to get started!") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, topics.map((topic) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: topic.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, topic.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium" }, topic.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, getSubjectName(topic.subjectCode), " (", topic.subjectCode, ")"), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(topic), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(topic), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Topic" : "Add New Topic")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Topic Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., ALG-BASICS", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Topic Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Algebra Basics" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedSubjectCode, onValueChange: setSelectedSubjectCode, disabled: subjects.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "subjectCode" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select a subject" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, subjects.map((subject) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: subject.code, value: subject.code }, subject.name, " (", subject.code, ")")))))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() || !selectedSubjectCode }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete topic "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139192
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Tag, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Topics"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(MetadataImportControls, { metadataName: "Topics", onImport: handleImport }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm", disabled: subjects.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Topic"))), subjects.length === 0 && !isLoading && /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-destructive" }, "Please add subjects before adding topics.")), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : topics.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No topics found. Add one to get started!") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, topics.map((topic) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: topic.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, topic.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, topic.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, getSubjectName(topic.subjectCode), " (", topic.subjectCode, ")"), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(topic), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(topic), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Topic" : "Add New Topic")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Topic Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., ALG-BASICS", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Topic Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Algebra Basics" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedSubjectCode, onValueChange: setSelectedSubjectCode, disabled: subjects.length === 0 }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "subjectCode" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select a subject" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, subjects.map((subject) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: subject.code, value: subject.code }, subject.name, " (", subject.code, ")")))))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() || !selectedSubjectCode }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete topic "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139043
139193
  }
139044
139194
 
139045
139195
  // src/react-ui/components/metadata/CategoryManager.tsx
139046
139196
  init_react_shim();
139047
- function CategoryManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
139197
+ function CategoryManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
139048
139198
  const [items, setItems] = React169.useState([]);
139049
139199
  const [isLoading, setIsLoading] = React169.useState(true);
139050
139200
  const [isDialogOpen, setIsDialogOpen] = React169.useState(false);
@@ -139144,12 +139294,37 @@ function CategoryManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdat
139144
139294
  }
139145
139295
  });
139146
139296
  };
139147
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Layers, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Categories"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Category"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No categories found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Category" : "Add New Category")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., CORE_CONCEPT", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Core Concept" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Fundamental ideas within a subject." }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139297
+ const handleImport = async (records) => {
139298
+ if (!onBulkAdd) return;
139299
+ const validationResult = records.reduce((acc, rec) => {
139300
+ if (typeof rec.code === "string" && rec.code.trim() && typeof rec.name === "string" && rec.name.trim()) {
139301
+ acc.valid.push({
139302
+ code: rec.code,
139303
+ name: rec.name,
139304
+ description: typeof rec.description === "string" ? rec.description : void 0
139305
+ });
139306
+ } else {
139307
+ acc.invalidCount++;
139308
+ }
139309
+ return acc;
139310
+ }, { valid: [], invalidCount: 0 });
139311
+ if (validationResult.invalidCount > 0) {
139312
+ toast2({
139313
+ title: "Import Warning",
139314
+ description: `${validationResult.invalidCount} records had invalid or missing 'code' or 'name' fields and were ignored.`,
139315
+ variant: "destructive"
139316
+ });
139317
+ }
139318
+ if (validationResult.valid.length > 0) {
139319
+ await onBulkAdd(validationResult.valid);
139320
+ }
139321
+ };
139322
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Layers, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Categories"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(MetadataImportControls, { metadataName: "Categories", onImport: handleImport }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Category")))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No categories found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Category" : "Add New Category")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., CORE_CONCEPT", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Core Concept" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Fundamental ideas within a subject." }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139148
139323
  }
139149
139324
 
139150
139325
  // src/react-ui/components/metadata/BloomLevelManager.tsx
139151
139326
  init_react_shim();
139152
- function BloomLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
139327
+ function BloomLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
139153
139328
  const [items, setItems] = React169.useState([]);
139154
139329
  const [isLoading, setIsLoading] = React169.useState(true);
139155
139330
  const [isDialogOpen, setIsDialogOpen] = React169.useState(false);
@@ -139249,12 +139424,37 @@ function BloomLevelManager({ initialData, isLoading: isLoadingProp, onAdd, onUpd
139249
139424
  }
139250
139425
  });
139251
139426
  };
139252
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Brain, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Bloom's Levels"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Bloom's Level"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Bloom's Levels found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Bloom's Level" : "Add New Bloom's Level")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., REMEMBER", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Remembering" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Recall facts and basic concepts." }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139427
+ const handleImport = async (records) => {
139428
+ if (!onBulkAdd) return;
139429
+ const validationResult = records.reduce((acc, rec) => {
139430
+ if (typeof rec.code === "string" && rec.code.trim() && typeof rec.name === "string" && rec.name.trim()) {
139431
+ acc.valid.push({
139432
+ code: rec.code,
139433
+ name: rec.name,
139434
+ description: typeof rec.description === "string" ? rec.description : void 0
139435
+ });
139436
+ } else {
139437
+ acc.invalidCount++;
139438
+ }
139439
+ return acc;
139440
+ }, { valid: [], invalidCount: 0 });
139441
+ if (validationResult.invalidCount > 0) {
139442
+ toast2({
139443
+ title: "Import Warning",
139444
+ description: `${validationResult.invalidCount} records had invalid or missing 'code' or 'name' fields and were ignored.`,
139445
+ variant: "destructive"
139446
+ });
139447
+ }
139448
+ if (validationResult.valid.length > 0) {
139449
+ await onBulkAdd(validationResult.valid);
139450
+ }
139451
+ };
139452
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Brain, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Bloom's Levels"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(MetadataImportControls, { metadataName: "Bloom's Levels", onImport: handleImport }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Bloom's Level")))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Bloom's Levels found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Bloom's Level" : "Add New Bloom's Level")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., REMEMBER", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Remembering" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Recall facts and basic concepts." }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139253
139453
  }
139254
139454
 
139255
139455
  // src/react-ui/components/metadata/QuestionTypeManager.tsx
139256
139456
  init_react_shim();
139257
- function QuestionTypeManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
139457
+ function QuestionTypeManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
139258
139458
  const [items, setItems] = React169.useState([]);
139259
139459
  const [isLoading, setIsLoading] = React169.useState(true);
139260
139460
  const [isDialogOpen, setIsDialogOpen] = React169.useState(false);
@@ -139324,6 +139524,31 @@ function QuestionTypeManager({ initialData, isLoading: isLoadingProp, onAdd, onU
139324
139524
  }
139325
139525
  });
139326
139526
  };
139527
+ const handleImport = async (records) => {
139528
+ if (!onBulkAdd) return;
139529
+ const validationResult = records.reduce((acc, rec) => {
139530
+ if (typeof rec.code === "string" && rec.code.trim() && typeof rec.name === "string" && rec.name.trim()) {
139531
+ acc.valid.push({
139532
+ code: rec.code,
139533
+ name: rec.name,
139534
+ description: typeof rec.description === "string" ? rec.description : void 0
139535
+ });
139536
+ } else {
139537
+ acc.invalidCount++;
139538
+ }
139539
+ return acc;
139540
+ }, { valid: [], invalidCount: 0 });
139541
+ if (validationResult.invalidCount > 0) {
139542
+ toast2({
139543
+ title: "Import Warning",
139544
+ description: `${validationResult.invalidCount} records had invalid or missing 'code' or 'name' fields and were ignored.`,
139545
+ variant: "destructive"
139546
+ });
139547
+ }
139548
+ if (validationResult.valid.length > 0) {
139549
+ await onBulkAdd(validationResult.valid);
139550
+ }
139551
+ };
139327
139552
  const handleSubmit = () => {
139328
139553
  if (!itemName.trim() || !itemCode.trim()) {
139329
139554
  toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
@@ -139354,22 +139579,19 @@ function QuestionTypeManager({ initialData, isLoading: isLoadingProp, onAdd, onU
139354
139579
  }
139355
139580
  });
139356
139581
  };
139357
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleHelp, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Question Types"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Question Type"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Question Types found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Question Type" : "Add New Question Type")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Multiple Choice" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139582
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(CircleHelp, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Question Types"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(MetadataImportControls, { metadataName: "Question Types", onImport: handleImport }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Question Type")))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Question Types found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Question Type" : "Add New Question Type")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Multiple Choice" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__namespace.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__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139358
139583
  }
139359
139584
 
139360
139585
  // src/react-ui/components/metadata/LearningObjectiveManager.tsx
139361
139586
  init_react_shim();
139362
- function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
139587
+ function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
139363
139588
  const [items, setItems] = React169.useState([]);
139364
139589
  const [subjects, setSubjects] = React169.useState([]);
139365
139590
  const [isLoading, setIsLoading] = React169.useState(true);
139366
139591
  const [isDialogOpen, setIsDialogOpen] = React169.useState(false);
139367
139592
  const [isAlertOpen, setIsAlertOpen] = React169.useState(false);
139368
139593
  const [currentItem, setCurrentItem] = React169.useState(null);
139369
- const [itemName, setItemName] = React169.useState("");
139370
- const [itemCode, setItemCode] = React169.useState("");
139371
- const [itemDescription, setItemDescription] = React169.useState("");
139372
- const [selectedSubjectCode, setSelectedSubjectCode] = React169.useState(void 0);
139594
+ const [formState, setFormState] = React169.useState({});
139373
139595
  const [itemToDelete, setItemToDelete] = React169.useState(null);
139374
139596
  const [isPending, startTransition] = React169.useTransition();
139375
139597
  const { toast: toast2 } = useToast();
@@ -139396,20 +139618,17 @@ function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoadi
139396
139618
  refreshData();
139397
139619
  }
139398
139620
  }, [isControlled, initialData, subjectsProp, isLoadingProp]);
139621
+ const handleFormChange = (field, value) => {
139622
+ setFormState((prev) => ({ ...prev, [field]: value }));
139623
+ };
139399
139624
  const handleAddItem = () => {
139400
139625
  setCurrentItem(null);
139401
- setItemName("");
139402
- setItemCode("");
139403
- setItemDescription("");
139404
- setSelectedSubjectCode(subjects.length > 0 ? subjects[0].code : void 0);
139626
+ setFormState({ subjectCode: subjects.length > 0 ? subjects[0].code : "" });
139405
139627
  setIsDialogOpen(true);
139406
139628
  };
139407
139629
  const handleEditItem = (item) => {
139408
139630
  setCurrentItem(item);
139409
- setItemName(item.name);
139410
- setItemCode(item.code);
139411
- setItemDescription(item.description || "");
139412
- setSelectedSubjectCode(item.subjectCode);
139631
+ setFormState(item);
139413
139632
  setIsDialogOpen(true);
139414
139633
  };
139415
139634
  const handleDeleteItem = (item) => {
@@ -139436,7 +139655,7 @@ function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoadi
139436
139655
  });
139437
139656
  };
139438
139657
  const handleSubmit = () => {
139439
- if (!itemName.trim() || !itemCode.trim()) {
139658
+ if (!formState.name?.trim() || !formState.code?.trim()) {
139440
139659
  toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
139441
139660
  return;
139442
139661
  }
@@ -139444,17 +139663,17 @@ function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoadi
139444
139663
  try {
139445
139664
  if (currentItem) {
139446
139665
  if (isControlled && onUpdate) {
139447
- await onUpdate({ id: currentItem.id, name: itemName, code: itemCode, subjectCode: selectedSubjectCode, description: itemDescription });
139666
+ await onUpdate({ ...currentItem, ...formState });
139448
139667
  } else {
139449
- MetadataService.updateLearningObjective(currentItem.id, itemName, itemCode, selectedSubjectCode, itemDescription);
139668
+ MetadataService.updateLearningObjective(currentItem.id, formState);
139450
139669
  refreshData();
139451
139670
  }
139452
139671
  toast2({ title: "Success", description: "Learning Objective updated." });
139453
139672
  } else {
139454
139673
  if (isControlled && onAdd) {
139455
- await onAdd({ name: itemName, code: itemCode, subjectCode: selectedSubjectCode, description: itemDescription });
139674
+ await onAdd(formState);
139456
139675
  } else {
139457
- MetadataService.addLearningObjective(itemName, itemCode, selectedSubjectCode, itemDescription);
139676
+ MetadataService.addLearningObjective(formState);
139458
139677
  refreshData();
139459
139678
  }
139460
139679
  toast2({ title: "Success", description: "Learning Objective added." });
@@ -139465,16 +139684,51 @@ function LearningObjectiveManager({ initialData, subjects: subjectsProp, isLoadi
139465
139684
  }
139466
139685
  });
139467
139686
  };
139468
- const getSubjectName = (subjectCode) => {
139469
- if (!subjectCode) return "N/A";
139470
- return subjects.find((s4) => s4.code === subjectCode)?.name || "N/A";
139687
+ const handleImport = async (records) => {
139688
+ if (!onBulkAdd) return;
139689
+ const parseStringToArray = (input) => {
139690
+ if (Array.isArray(input)) return input;
139691
+ if (typeof input === "string") return input.split(",").map((s4) => s4.trim()).filter(Boolean);
139692
+ return [];
139693
+ };
139694
+ const validatedRecords = records.reduce((acc, rec) => {
139695
+ if (typeof rec.code === "string" && rec.code.trim() && typeof rec.name === "string" && rec.name.trim()) {
139696
+ acc.push({
139697
+ code: rec.code,
139698
+ name: rec.name,
139699
+ description: rec.description,
139700
+ subject: rec.subject,
139701
+ subjectCode: rec.subjectCode,
139702
+ category: rec.category,
139703
+ categoryCode: rec.categoryCode,
139704
+ topic: rec.topic,
139705
+ topicCode: rec.topicCode,
139706
+ grade: rec.grade,
139707
+ gradeCode: rec.gradeCode,
139708
+ keywords: parseStringToArray(rec.keywords),
139709
+ stemElements: parseStringToArray(rec.stemElements),
139710
+ bloomLevelsGuideline: parseStringToArray(rec.bloomLevelsGuideline)
139711
+ });
139712
+ }
139713
+ return acc;
139714
+ }, []);
139715
+ if (validatedRecords.length !== records.length) {
139716
+ toast2({
139717
+ title: "Import Warning",
139718
+ description: `${records.length - validatedRecords.length} records had invalid or missing required fields ('code', 'name') and were ignored.`,
139719
+ variant: "destructive"
139720
+ });
139721
+ }
139722
+ if (validatedRecords.length > 0) {
139723
+ await onBulkAdd(validatedRecords);
139724
+ }
139471
139725
  };
139472
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Lightbulb, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Learning Objectives"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Learning Objective"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Learning Objectives found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, getSubjectName(item.subjectCode)), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Learning Objective" : "Add New Learning Objective")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value) })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemSubject" }, "Subject (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: selectedSubjectCode || "", onValueChange: (value) => setSelectedSubjectCode(value === "_NONE_" ? void 0 : value) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, { id: "itemSubject" }, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select a subject" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { value: "_NONE_" }, "No Specific Subject"), subjects.map((subject) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: subject.id, value: subject.code }, subject.name))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value) }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139726
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Lightbulb, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Learning Objectives"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(MetadataImportControls, { metadataName: "Learning Objectives", onImport: handleImport }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Learning Objective")))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Learning Objectives found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Topic"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.subject || item.subjectCode), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.topic || item.topicCode), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Learning Objective" : "Add New Learning Objective")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "code" }, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "code", value: formState.code || "", onChange: (e3) => handleFormChange("code", e3.target.value.toUpperCase()), disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "name" }, "Name (Description)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "name", value: formState.name || "", onChange: (e3) => handleFormChange("name", e3.target.value) }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.subjectCode || "", onValueChange: (value) => handleFormChange("subjectCode", value) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, { placeholder: "Select a subject" })), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, subjects.map((subject) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: subject.id, value: subject.code }, subject.name))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "topicCode" }, "Topic Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "topicCode", value: formState.topicCode || "", onChange: (e3) => handleFormChange("topicCode", e3.target.value) }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "categoryCode" }, "Category Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "categoryCode", value: formState.categoryCode || "", onChange: (e3) => handleFormChange("categoryCode", e3.target.value) })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "gradeCode" }, "Grade Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "gradeCode", value: formState.gradeCode || "", onChange: (e3) => handleFormChange("gradeCode", e3.target.value) }))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "keywords" }, "Keywords (comma-separated)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "keywords", value: formState.keywords?.join(", ") || "", onChange: (e3) => handleFormChange("keywords", e3.target.value.split(",").map((s4) => s4.trim())) }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !formState.name?.trim() || !formState.code?.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139473
139727
  }
139474
139728
 
139475
139729
  // src/react-ui/components/metadata/ContextManager.tsx
139476
139730
  init_react_shim();
139477
- function ContextManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }) {
139731
+ function ContextManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete, onBulkAdd }) {
139478
139732
  const [items, setItems] = React169.useState([]);
139479
139733
  const [isLoading, setIsLoading] = React169.useState(true);
139480
139734
  const [isDialogOpen, setIsDialogOpen] = React169.useState(false);
@@ -139544,6 +139798,31 @@ function ContextManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate
139544
139798
  }
139545
139799
  });
139546
139800
  };
139801
+ const handleImport = async (records) => {
139802
+ if (!onBulkAdd) return;
139803
+ const validationResult = records.reduce((acc, rec) => {
139804
+ if (typeof rec.code === "string" && rec.code.trim() && typeof rec.name === "string" && rec.name.trim()) {
139805
+ acc.valid.push({
139806
+ code: rec.code,
139807
+ name: rec.name,
139808
+ description: typeof rec.description === "string" ? rec.description : void 0
139809
+ });
139810
+ } else {
139811
+ acc.invalidCount++;
139812
+ }
139813
+ return acc;
139814
+ }, { valid: [], invalidCount: 0 });
139815
+ if (validationResult.invalidCount > 0) {
139816
+ toast2({
139817
+ title: "Import Warning",
139818
+ description: `${validationResult.invalidCount} records had invalid or missing 'code' or 'name' fields and were ignored.`,
139819
+ variant: "destructive"
139820
+ });
139821
+ }
139822
+ if (validationResult.valid.length > 0) {
139823
+ await onBulkAdd(validationResult.valid);
139824
+ }
139825
+ };
139547
139826
  const handleSubmit = () => {
139548
139827
  if (!itemName.trim() || !itemCode.trim()) {
139549
139828
  toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
@@ -139574,13 +139853,13 @@ function ContextManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate
139574
139853
  }
139575
139854
  });
139576
139855
  };
139577
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(ScanText, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Contexts"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Context"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Contexts found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Context" : "Add New Context")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., HIST_INQ", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Historical Inquiry" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Analyzing primary and secondary sources." }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139856
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(ScanText, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Contexts"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(MetadataImportControls, { metadataName: "Categories", onImport: handleImport }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Context")))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Contexts found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.description), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Context" : "Add New Context")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e3) => setItemCode(e3.target.value.toUpperCase()), placeholder: "e.g., HIST_INQ", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "itemName", value: itemName, onChange: (e3) => setItemName(e3.target.value), placeholder: "e.g., Historical Inquiry" })), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e3) => setItemDescription(e3.target.value), placeholder: "e.g., Analyzing primary and secondary sources." }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.name, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139578
139857
  }
139579
139858
 
139580
139859
  // src/react-ui/components/metadata/ApproachManager.tsx
139581
139860
  init_react_shim();
139582
139861
  var knowledgeDimensions = ["Factual", "Conceptual", "Procedural"];
139583
- var rawDifficultyLevels = ["E", "E~M", "M", "M~H", "H"];
139862
+ var standardDifficulties = ["Easy", "Medium", "Hard"];
139584
139863
  function ApproachManager({
139585
139864
  initialData,
139586
139865
  bloomLevels: bloomLevelsProp,
@@ -139588,7 +139867,8 @@ function ApproachManager({
139588
139867
  isLoading: isLoadingProp,
139589
139868
  onAdd,
139590
139869
  onUpdate,
139591
- onDelete
139870
+ onDelete,
139871
+ onBulkAdd
139592
139872
  }) {
139593
139873
  const [items, setItems] = React169.useState([]);
139594
139874
  const [bloomLevels, setBloomLevels] = React169.useState([]);
@@ -139629,7 +139909,7 @@ function ApproachManager({
139629
139909
  const resetForm = () => {
139630
139910
  setFormState({
139631
139911
  knowledgeDimension: "Factual",
139632
- rawDifficulty: "E",
139912
+ difficulty: ["Medium"],
139633
139913
  bloomLevelCode: bloomLevels.length > 0 ? bloomLevels[0].code : "",
139634
139914
  iSpringQuizType: questionTypes.length > 0 ? questionTypes[0].code : "multiple_choice"
139635
139915
  });
@@ -139641,18 +139921,7 @@ function ApproachManager({
139641
139921
  };
139642
139922
  const handleEditItem = (item) => {
139643
139923
  setCurrentItem(item);
139644
- setFormState({
139645
- code: item.code,
139646
- verbEn: item.verbEn,
139647
- verbVi: item.verbVi,
139648
- bloomLevelCode: item.bloomLevelCode,
139649
- knowledgeDimension: item.knowledgeDimension,
139650
- iSpringQuizType: item.iSpringQuizType,
139651
- rawDifficulty: item.rawDifficulty,
139652
- suggestContext: item.suggestContext,
139653
- exampleEn: item.exampleEn,
139654
- exampleVi: item.exampleVi
139655
- });
139924
+ setFormState(item);
139656
139925
  setIsDialogOpen(true);
139657
139926
  };
139658
139927
  const handleDeleteItem = (item) => {
@@ -139679,9 +139948,9 @@ function ApproachManager({
139679
139948
  });
139680
139949
  };
139681
139950
  const handleSubmit = () => {
139682
- const { code: code4, verbEn, verbVi, bloomLevelCode, iSpringQuizType, knowledgeDimension, rawDifficulty } = formState;
139683
- if (!code4?.trim() || !verbEn?.trim() || !verbVi?.trim() || !bloomLevelCode || !iSpringQuizType || !knowledgeDimension || !rawDifficulty) {
139684
- toast2({ title: "Validation Error", description: "All fields except examples and context are required.", variant: "destructive" });
139951
+ const { code: code4, name: name3, verbEn, verbVi, bloomLevelCode, iSpringQuizType, knowledgeDimension, difficulty } = formState;
139952
+ if (!code4?.trim() || !name3?.trim() || !verbEn?.trim() || !verbVi?.trim() || !bloomLevelCode || !iSpringQuizType || !knowledgeDimension || !difficulty || difficulty.length === 0) {
139953
+ toast2({ title: "Validation Error", description: "All fields except examples and context are required, and at least one difficulty must be selected.", variant: "destructive" });
139685
139954
  return;
139686
139955
  }
139687
139956
  startTransition(async () => {
@@ -139709,7 +139978,47 @@ function ApproachManager({
139709
139978
  }
139710
139979
  });
139711
139980
  };
139712
- return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Settings2, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Approaches"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Approach"))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Approaches found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Approach ID"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Verb (VI)"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Cognitive Level"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "iSpring Type"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-medium" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.verbVi), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.bloomLevelCode), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.iSpringQuizType), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Approach" : "Add New Approach")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "code" }, "Approach Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "code", value: formState.code || "", onChange: (e3) => setFormState((s4) => ({ ...s4, code: e3.target.value.toUpperCase() })), placeholder: "e.g., REM-FAC-IDT-MCQ", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "verbEn" }, "Verb (English)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "verbEn", value: formState.verbEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, verbEn: e3.target.value })), placeholder: "e.g., Identify" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "verbVi" }, "Verb (Vietnamese)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "verbVi", value: formState.verbVi || "", onChange: (e3) => setFormState((s4) => ({ ...s4, verbVi: e3.target.value })), placeholder: "e.g., Nh\u1EADn d\u1EA1ng" })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "bloomLevelCode" }, "Cognitive Level"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.bloomLevelCode, onValueChange: (v) => setFormState((s4) => ({ ...s4, bloomLevelCode: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, bloomLevels.map((level) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: level.code, value: level.code }, level.name)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "knowledgeDimension" }, "Knowledge Dimension"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.knowledgeDimension, onValueChange: (v) => setFormState((s4) => ({ ...s4, knowledgeDimension: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, knowledgeDimensions.map((kd) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: kd, value: kd }, kd))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "iSpringQuizType" }, "iSpring Quiz Type"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.iSpringQuizType, onValueChange: (v) => setFormState((s4) => ({ ...s4, iSpringQuizType: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, questionTypes.map((qt) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: qt.code, value: qt.code }, qt.name))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "rawDifficulty" }, "Raw Difficulty"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.rawDifficulty, onValueChange: (v) => setFormState((s4) => ({ ...s4, rawDifficulty: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, rawDifficultyLevels.map((rd) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: rd, value: rd }, rd)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "suggestContext" }, "Suggest Context (comma-separated codes)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "suggestContext", value: formState.suggestContext || "", onChange: (e3) => setFormState((s4) => ({ ...s4, suggestContext: e3.target.value })), placeholder: "e.g., A, B, D, G, H" })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "exampleEn" }, "Example (English)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "exampleEn", value: formState.exampleEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, exampleEn: e3.target.value })), placeholder: "English example prompt..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "exampleVi" }, "Example (Vietnamese)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "exampleVi", value: formState.exampleVi || "", onChange: (e3) => setFormState((s4) => ({ ...s4, exampleVi: e3.target.value })), placeholder: "Vietnamese example prompt..." }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.code, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139981
+ const handleImport = async (records) => {
139982
+ if (!onBulkAdd) return;
139983
+ const parseStringToArray = (input) => {
139984
+ if (Array.isArray(input)) return input;
139985
+ if (typeof input === "string") return input.split(",").map((s4) => s4.trim()).filter(Boolean);
139986
+ return [];
139987
+ };
139988
+ const validatedRecords = records.reduce((acc, rec) => {
139989
+ if (rec.code && rec.name && rec.verbEn && rec.verbVi && rec.knowledgeDimension && rec.iSpringQuizType && rec.difficulty && rec.bloomLevelCode) {
139990
+ acc.push({
139991
+ code: rec.code,
139992
+ name: rec.name,
139993
+ verbEn: rec.verbEn,
139994
+ verbVi: rec.verbVi,
139995
+ knowledgeDimension: rec.knowledgeDimension,
139996
+ iSpringQuizType: rec.iSpringQuizType,
139997
+ difficulty: parseStringToArray(rec.difficulty),
139998
+ bloomLevelCode: rec.bloomLevelCode,
139999
+ suggestContext: rec.suggestContext,
140000
+ exampleEn: rec.exampleEn,
140001
+ exampleVi: rec.exampleVi
140002
+ });
140003
+ }
140004
+ return acc;
140005
+ }, []);
140006
+ if (validatedRecords.length !== records.length) {
140007
+ toast2({
140008
+ title: "Import Warning",
140009
+ description: `${records.length - validatedRecords.length} records had missing required fields and were ignored.`,
140010
+ variant: "destructive"
140011
+ });
140012
+ }
140013
+ if (validatedRecords.length > 0) {
140014
+ await onBulkAdd(validatedRecords);
140015
+ }
140016
+ };
140017
+ return /* @__PURE__ */ React169__namespace.default.createElement(Card, null, /* @__PURE__ */ React169__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React169__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React169__namespace.default.createElement(Settings2, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Approaches"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center gap-2" }, onBulkAdd && /* @__PURE__ */ React169__namespace.default.createElement(MetadataImportControls, { metadataName: "Approaches", onImport: handleImport }), /* @__PURE__ */ React169__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React169__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Approach")))), /* @__PURE__ */ React169__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Approaches found.") : /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(Table3, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Approach ID"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Verb (VI)"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, null, "Cognitive Level"), /* @__PURE__ */ React169__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React169__namespace.default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React169__namespace.default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "font-Medium" }, item.code), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.name), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.verbVi), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, null, item.bloomLevelCode), /* @__PURE__ */ React169__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React169__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React169__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, { className: "sm:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, currentItem ? "Edit Approach" : "Add New Approach")), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "code" }, "Approach Code"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "code", value: formState.code || "", onChange: (e3) => setFormState((s4) => ({ ...s4, code: e3.target.value.toUpperCase() })), placeholder: "e.g., REM-FAC-IDT-MCQ", disabled: !!currentItem })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "name" }, "Name / Description"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "name", value: formState.name || "", onChange: (e3) => setFormState((s4) => ({ ...s4, name: e3.target.value })), placeholder: "e.g., Identify a Fact" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "verbEn" }, "Verb (English)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "verbEn", value: formState.verbEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, verbEn: e3.target.value })), placeholder: "e.g., Identify" })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "verbVi" }, "Verb (Vietnamese)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "verbVi", value: formState.verbVi || "", onChange: (e3) => setFormState((s4) => ({ ...s4, verbVi: e3.target.value })), placeholder: "e.g., Nh\u1EADn d\u1EA1ng" }))), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "bloomLevelCode" }, "Cognitive Level"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.bloomLevelCode, onValueChange: (v) => setFormState((s4) => ({ ...s4, bloomLevelCode: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, bloomLevels.map((level) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: level.code, value: level.code }, level.name))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "knowledgeDimension" }, "Knowledge Dimension"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.knowledgeDimension, onValueChange: (v) => setFormState((s4) => ({ ...s4, knowledgeDimension: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, knowledgeDimensions.map((kd) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: kd, value: kd }, kd))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "iSpringQuizType" }, "iSpring Quiz Type"), /* @__PURE__ */ React169__namespace.default.createElement(Select2, { value: formState.iSpringQuizType, onValueChange: (v) => setFormState((s4) => ({ ...s4, iSpringQuizType: v })) }, /* @__PURE__ */ React169__namespace.default.createElement(SelectTrigger2, null, /* @__PURE__ */ React169__namespace.default.createElement(SelectValue2, null)), /* @__PURE__ */ React169__namespace.default.createElement(SelectContent2, null, questionTypes.map((qt) => /* @__PURE__ */ React169__namespace.default.createElement(SelectItem2, { key: qt.code, value: qt.code }, qt.name)))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, null, "Difficulty"), /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "flex items-center space-x-4 pt-2" }, standardDifficulties.map((diff2) => /* @__PURE__ */ React169__namespace.default.createElement("div", { key: diff2, className: "flex items-center space-x-2" }, /* @__PURE__ */ React169__namespace.default.createElement(Checkbox2, { id: `diff-${diff2}`, checked: (formState.difficulty || []).includes(diff2), onCheckedChange: (checked) => {
140018
+ const current = formState.difficulty || [];
140019
+ const newDiff = checked ? [...current, diff2] : current.filter((d) => d !== diff2);
140020
+ setFormState((s4) => ({ ...s4, difficulty: newDiff }));
140021
+ } }), /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: `diff-${diff2}` }, diff2))))), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "suggestContext" }, "Suggest Context (comma-separated codes)"), /* @__PURE__ */ React169__namespace.default.createElement(Input, { id: "suggestContext", value: formState.suggestContext || "", onChange: (e3) => setFormState((s4) => ({ ...s4, suggestContext: e3.target.value })), placeholder: "e.g., A, B, D, G, H" })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "exampleEn" }, "Example (English)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "exampleEn", value: formState.exampleEn || "", onChange: (e3) => setFormState((s4) => ({ ...s4, exampleEn: e3.target.value })), placeholder: "English example prompt..." })), /* @__PURE__ */ React169__namespace.default.createElement("div", null, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "exampleVi" }, "Example (Vietnamese)"), /* @__PURE__ */ React169__namespace.default.createElement(Textarea, { id: "exampleVi", value: formState.exampleVi || "", onChange: (e3) => setFormState((s4) => ({ ...s4, exampleVi: e3.target.value })), placeholder: "Vietnamese example prompt..." }))), /* @__PURE__ */ React169__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogDescription2, null, 'This will permanently delete "', itemToDelete?.code, '".')), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React169__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
139713
140022
  }
139714
140023
 
139715
140024
  // src/react-ui/components/metadata/MetadataTabs.tsx
@@ -140385,7 +140694,7 @@ var ToastAction2 = React169__namespace.forwardRef(({ className, ...props }, ref)
140385
140694
  {
140386
140695
  ref,
140387
140696
  className: cn(
140388
- "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",
140697
+ "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",
140389
140698
  className
140390
140699
  ),
140391
140700
  ...props
@@ -142023,6 +142332,7 @@ exports.Label = Label2;
142023
142332
  exports.LanguageProvider = LanguageProvider;
142024
142333
  exports.LearningObjectiveManager = LearningObjectiveManager;
142025
142334
  exports.ManageTopics = ManageTopics;
142335
+ exports.MetadataImportControls = MetadataImportControls;
142026
142336
  exports.MetadataTabs = MetadataTabs;
142027
142337
  exports.PerformanceCharts = PerformanceCharts;
142028
142338
  exports.PersonalPracticeDashboard = PersonalPracticeDashboard;