@thanh01.pmt/interactive-quiz-kit 1.0.48 → 1.0.50

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.
@@ -4077,6 +4077,14 @@ var PracticeHistoryService = class {
4077
4077
  }
4078
4078
  static getPracticeStats() {
4079
4079
  const history2 = this.getPracticeHistory();
4080
+ return this.calculateStatsFromHistory(history2);
4081
+ }
4082
+ static clearHistory() {
4083
+ if (typeof window !== "undefined") {
4084
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
4085
+ }
4086
+ }
4087
+ static calculateStatsFromHistory(history2) {
4080
4088
  if (history2.length === 0) {
4081
4089
  return {
4082
4090
  totalSessions: 0,
@@ -4185,11 +4193,6 @@ var PracticeHistoryService = class {
4185
4193
  performanceByTopic: formatPerf(topicPerf)
4186
4194
  };
4187
4195
  }
4188
- static clearHistory() {
4189
- if (typeof window !== "undefined") {
4190
- localStorage.removeItem(LOCAL_STORAGE_KEY);
4191
- }
4192
- }
4193
4196
  };
4194
4197
  // NEW: A static property to hold the injected sync provider
4195
4198
  PracticeHistoryService.syncProvider = null;
@@ -105538,7 +105541,13 @@ init_react_shim();
105538
105541
 
105539
105542
  // src/react-ui/components/metadata/SubjectManager.tsx
105540
105543
  init_react_shim();
105541
- function SubjectManager() {
105544
+ function SubjectManager({
105545
+ initialData,
105546
+ isLoading: isLoadingProp,
105547
+ onAdd,
105548
+ onUpdate,
105549
+ onDelete
105550
+ }) {
105542
105551
  const [subjects, setSubjects] = React97.useState([]);
105543
105552
  const [isLoading, setIsLoading] = React97.useState(true);
105544
105553
  const [isDialogOpen, setIsDialogOpen] = React97.useState(false);
@@ -105549,20 +105558,27 @@ function SubjectManager() {
105549
105558
  const [itemToDelete, setItemToDelete] = React97.useState(null);
105550
105559
  const [isPending, startTransition] = React97.useTransition();
105551
105560
  const { toast: toast2 } = useToast();
105552
- React97.useEffect(() => {
105553
- setIsLoading(true);
105554
- try {
105555
- const data = MetadataService.getSubjects();
105556
- setSubjects(data);
105557
- } catch (error) {
105558
- toast2({ title: "Error", description: "Failed to fetch subjects from local storage.", variant: "destructive" });
105559
- } finally {
105560
- setIsLoading(false);
105561
- }
105562
- }, []);
105561
+ const isControlled = initialData !== void 0;
105563
105562
  const refreshData = () => {
105564
- setSubjects(MetadataService.getSubjects());
105563
+ if (!isControlled) {
105564
+ setIsLoading(true);
105565
+ try {
105566
+ setSubjects(MetadataService.getSubjects());
105567
+ } catch (error) {
105568
+ toast2({ title: "Error", description: "Failed to refresh subjects.", variant: "destructive" });
105569
+ } finally {
105570
+ setIsLoading(false);
105571
+ }
105572
+ }
105565
105573
  };
105574
+ React97.useEffect(() => {
105575
+ if (isControlled) {
105576
+ setSubjects(initialData || []);
105577
+ setIsLoading(isLoadingProp || false);
105578
+ } else {
105579
+ refreshData();
105580
+ }
105581
+ }, [isControlled, initialData, isLoadingProp]);
105566
105582
  const handleAddItem = () => {
105567
105583
  setCurrentSubject(null);
105568
105584
  setSubjectName("");
@@ -105581,11 +105597,15 @@ function SubjectManager() {
105581
105597
  };
105582
105598
  const confirmDelete = () => {
105583
105599
  if (!itemToDelete) return;
105584
- startTransition(() => {
105600
+ startTransition(async () => {
105585
105601
  try {
105586
- MetadataService.deleteSubject(itemToDelete.code);
105602
+ if (isControlled && onDelete) {
105603
+ await onDelete(itemToDelete);
105604
+ } else {
105605
+ MetadataService.deleteSubject(itemToDelete.code);
105606
+ refreshData();
105607
+ }
105587
105608
  toast2({ title: "Success", description: `Subject "${itemToDelete.name}" deleted.` });
105588
- refreshData();
105589
105609
  } catch (error) {
105590
105610
  toast2({ title: "Error", description: error.message || "Failed to delete subject.", variant: "destructive" });
105591
105611
  } finally {
@@ -105599,40 +105619,32 @@ function SubjectManager() {
105599
105619
  toast2({ title: "Validation Error", description: "Please enter Subject Name and Subject Code.", variant: "destructive" });
105600
105620
  return;
105601
105621
  }
105602
- startTransition(() => {
105622
+ startTransition(async () => {
105603
105623
  try {
105604
105624
  if (currentSubject) {
105605
- MetadataService.updateSubject(currentSubject.id, subjectName, subjectCode);
105625
+ if (isControlled && onUpdate) {
105626
+ await onUpdate({ id: currentSubject.id, name: subjectName, code: subjectCode });
105627
+ } else {
105628
+ MetadataService.updateSubject(currentSubject.id, subjectName, subjectCode);
105629
+ refreshData();
105630
+ }
105606
105631
  toast2({ title: "Success", description: "Subject updated." });
105607
105632
  } else {
105608
- MetadataService.addSubject(subjectName, subjectCode);
105633
+ if (isControlled && onAdd) {
105634
+ await onAdd({ name: subjectName, code: subjectCode });
105635
+ } else {
105636
+ MetadataService.addSubject(subjectName, subjectCode);
105637
+ refreshData();
105638
+ }
105609
105639
  toast2({ title: "Success", description: "Subject added." });
105610
105640
  }
105611
- refreshData();
105612
105641
  setIsDialogOpen(false);
105613
105642
  } catch (error) {
105614
105643
  toast2({ title: "Error", description: error.message || "Failed to save subject.", variant: "destructive" });
105615
105644
  }
105616
105645
  });
105617
105646
  };
105618
- return /* @__PURE__ */ React97__namespace.default.createElement(Card, null, /* @__PURE__ */ React97__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React97__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React97__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React97__namespace.default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React97__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React97__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React97__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React97__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React97__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React97__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React97__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React97__namespace.default.createElement(Table2, null, /* @__PURE__ */ React97__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React97__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React97__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React97__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React97__namespace.default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React97__namespace.default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React97__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React97__namespace.default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React97__namespace.default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React97__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React97__namespace.default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React97__namespace.default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React97__namespace.default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React97__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React97__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React97__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React97__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React97__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React97__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React97__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React97__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React97__namespace.default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React97__namespace.default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React97__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React97__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React97__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React97__namespace.default.createElement(
105619
- Input,
105620
- {
105621
- id: "subjectCode",
105622
- value: subjectCode,
105623
- onChange: (e2) => setSubjectCode(e2.target.value.toUpperCase()),
105624
- placeholder: "e.g., MATH",
105625
- disabled: !!currentSubject
105626
- }
105627
- )), /* @__PURE__ */ React97__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React97__namespace.default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React97__namespace.default.createElement(
105628
- Input,
105629
- {
105630
- id: "subjectName",
105631
- value: subjectName,
105632
- onChange: (e2) => setSubjectName(e2.target.value),
105633
- placeholder: "e.g., Mathematics"
105634
- }
105635
- ))), /* @__PURE__ */ React97__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React97__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React97__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React97__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Save")))), /* @__PURE__ */ React97__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '" (Code: ', itemToDelete?.code, "). Ensure no topics, questions, or learning objectives reference this subject.")), /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React97__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Delete"))))));
105647
+ return /* @__PURE__ */ React97__namespace.default.createElement(Card, null, /* @__PURE__ */ React97__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React97__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React97__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React97__namespace.default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React97__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React97__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React97__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React97__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React97__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React97__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React97__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React97__namespace.default.createElement(Table2, null, /* @__PURE__ */ React97__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React97__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React97__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React97__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React97__namespace.default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React97__namespace.default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React97__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React97__namespace.default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React97__namespace.default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React97__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React97__namespace.default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React97__namespace.default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React97__namespace.default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React97__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React97__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React97__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React97__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React97__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React97__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React97__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React97__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React97__namespace.default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React97__namespace.default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React97__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React97__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React97__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React97__namespace.default.createElement(Input, { id: "subjectCode", value: subjectCode, onChange: (e2) => setSubjectCode(e2.target.value.toUpperCase()), placeholder: "e.g., MATH", disabled: !!currentSubject })), /* @__PURE__ */ React97__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React97__namespace.default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React97__namespace.default.createElement(Input, { id: "subjectName", value: subjectName, onChange: (e2) => setSubjectName(e2.target.value), placeholder: "e.g., Mathematics" }))), /* @__PURE__ */ React97__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React97__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React97__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React97__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React97__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '".')), /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React97__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React97__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
105636
105648
  }
105637
105649
 
105638
105650
  // src/react-ui/components/metadata/GradeLevelManager.tsx
@@ -2,7 +2,7 @@ export { B as BaseQuestion, e as BlocklyProgrammingQuestion, C as CodingQuestion
2
2
  export { APIKeyService, AchievementService, Approach, ApproachTableRawDifficulty, BloomLevelName, BloomLevelType, Category, CodeNamedEntity, Context, GEMINI_API_KEY_SERVICE_NAME, GradeLevel, KnowledgeCardService, KnowledgeDimension, LearningObjective, LearningObjectiveMetadata, MetadataService, PracticeHistoryService, QuestionBankService, QuestionImportService, QuestionInBank, QuestionTypeType, QuizEditorService, QuizEngine, QuizEngineCallbacks, QuizEngineConstructorOptions, QuoteService, SCORMService, StandardDifficulty, Subject, Topic, UserConfigService, cn, emptyQuiz, exportQuizAsSCORMZip, generateLauncherHTML, generateSCORMManifest, generateUniqueId, sampleQuiz } from './index.cjs';
3
3
  export { i as Achievement, j as AchievementDefinition, r as ActivityCalendarData, u as AnalysisReport, A as AnswerDetail, x as ChatContext, C as ChatMessage, v as DashboardCardConfig, D as DashboardCardId, w as DashboardLayout, y as Goal, G as GoalType, t as ImageContextItem, I as ImportError, K as KnowledgeCard, L as LearningAnalysis, e as PerformanceByBloomLevel, b as PerformanceByCategory, d as PerformanceByDifficulty, P as PerformanceByLearningObjective, c as PerformanceByTopic, f as PerformanceMetric, s as PerformanceSummary, k as PracticeDifficulty, n as PracticeSession, p as PracticeSessionSummary, o as PracticeStats, m as PracticeSuggestion, l as PracticeSuggestionTopic, q as PracticeTopicSummary, g as QuestionReview, Q as QuizResultType, h as QuizReviewContent, R as RoadmapItem, T as TestCaseResult, U as UserAnswerType, a as UserAnswers, W as WeeklyRoadmap } from './ai-ecosystem-DqFRlFU3.cjs';
4
4
  export { AssessAndMapDocumentClientInput, AssessAndMapDocumentOutput, BloomLevelStringsForAI, GenerateCodingQuestionClientInput, GenerateCodingQuestionOutput, GenerateFillInTheBlanksQuestionClientInput, GenerateFillInTheBlanksQuestionOutput, GenerateLearningAnalysisClientInput, GenerateLearningAnalysisOutput, GenerateMCQQuestionClientInput, GenerateMCQQuestionOutput, GenerateMRQQuestionClientInput, GenerateMRQQuestionOutput, GenerateMatchingQuestionClientInput, GenerateMatchingQuestionOutput, GenerateMotivationalQuoteClientInput, GenerateMotivationalQuoteOutput, GenerateNumericQuestionClientInput, GenerateNumericQuestionOutput, GeneratePracticeSuggestionClientInput, GeneratePracticeSuggestionOutput, GenerateQuestionsFromQuizPlanClientInput, GenerateQuestionsFromQuizPlanOutput, GenerateQuizFromTextClientInput, GenerateQuizFromTextOutput, GenerateQuizPlanClientInput, GenerateQuizPlanOutput, GenerateQuizReviewClientInput, GenerateQuizReviewOutput, GenerateSequenceQuestionClientInput, GenerateSequenceQuestionOutput, GenerateShortAnswerQuestionClientInput, GenerateShortAnswerQuestionOutput, GenerateSingleKnowledgeCardClientInput, GenerateSingleKnowledgeCardOutput, GenerateTrueFalseQuestionClientInput, GenerateTrueFalseQuestionOutput, PlanKnowledgeCardsClientInput, PlanKnowledgeCardsOutput, PlannedQuestion, assessAndMapDocument, generateCodingQuestion, generateFillInTheBlanksQuestion, generateLearningAnalysis, generateMCQQuestion, generateMRQQuestion, generateMatchingQuestion, generateMotivationalQuote, generateNumericQuestion, generatePracticeSuggestion, generateQuestionsFromQuizPlan, generateQuizFromText, generateQuizPlan, generateQuizReview, generateSequenceQuestion, generateShortAnswerQuestion, generateSingleKnowledgeCard, generateTrueFalseQuestion, planKnowledgeCards } from './ai.cjs';
5
- export { a as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, b as APIKeyManagerModal, c as ApiKeySettings, j as ApproachManager, B as BloomLevelManager, C as CategoryManager, i as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, M as MetadataTabs, e as QuestionFilters, f as QuestionFormDialog, d as QuestionList, h as QuestionTypeManager, Q as QuizAuthoringTool, S as SCORMExportModal, g as SubjectManager, k as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-DfLqsPwa.cjs';
5
+ export { a as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, b as APIKeyManagerModal, c as ApiKeySettings, j as ApproachManager, B as BloomLevelManager, C as CategoryManager, i as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, M as MetadataTabs, e as QuestionFilters, f as QuestionFormDialog, d as QuestionList, h as QuestionTypeManager, Q as QuizAuthoringTool, S as SCORMExportModal, g as SubjectManager, k as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-UW4hDEhn.cjs';
6
6
  import 'clsx';
7
7
  import 'zod';
8
8
  import 'react';
@@ -2,7 +2,7 @@ export { B as BaseQuestion, e as BlocklyProgrammingQuestion, C as CodingQuestion
2
2
  export { APIKeyService, AchievementService, Approach, ApproachTableRawDifficulty, BloomLevelName, BloomLevelType, Category, CodeNamedEntity, Context, GEMINI_API_KEY_SERVICE_NAME, GradeLevel, KnowledgeCardService, KnowledgeDimension, LearningObjective, LearningObjectiveMetadata, MetadataService, PracticeHistoryService, QuestionBankService, QuestionImportService, QuestionInBank, QuestionTypeType, QuizEditorService, QuizEngine, QuizEngineCallbacks, QuizEngineConstructorOptions, QuoteService, SCORMService, StandardDifficulty, Subject, Topic, UserConfigService, cn, emptyQuiz, exportQuizAsSCORMZip, generateLauncherHTML, generateSCORMManifest, generateUniqueId, sampleQuiz } from './index.js';
3
3
  export { i as Achievement, j as AchievementDefinition, r as ActivityCalendarData, u as AnalysisReport, A as AnswerDetail, x as ChatContext, C as ChatMessage, v as DashboardCardConfig, D as DashboardCardId, w as DashboardLayout, y as Goal, G as GoalType, t as ImageContextItem, I as ImportError, K as KnowledgeCard, L as LearningAnalysis, e as PerformanceByBloomLevel, b as PerformanceByCategory, d as PerformanceByDifficulty, P as PerformanceByLearningObjective, c as PerformanceByTopic, f as PerformanceMetric, s as PerformanceSummary, k as PracticeDifficulty, n as PracticeSession, p as PracticeSessionSummary, o as PracticeStats, m as PracticeSuggestion, l as PracticeSuggestionTopic, q as PracticeTopicSummary, g as QuestionReview, Q as QuizResultType, h as QuizReviewContent, R as RoadmapItem, T as TestCaseResult, U as UserAnswerType, a as UserAnswers, W as WeeklyRoadmap } from './ai-ecosystem-DqVlSO3r.js';
4
4
  export { AssessAndMapDocumentClientInput, AssessAndMapDocumentOutput, BloomLevelStringsForAI, GenerateCodingQuestionClientInput, GenerateCodingQuestionOutput, GenerateFillInTheBlanksQuestionClientInput, GenerateFillInTheBlanksQuestionOutput, GenerateLearningAnalysisClientInput, GenerateLearningAnalysisOutput, GenerateMCQQuestionClientInput, GenerateMCQQuestionOutput, GenerateMRQQuestionClientInput, GenerateMRQQuestionOutput, GenerateMatchingQuestionClientInput, GenerateMatchingQuestionOutput, GenerateMotivationalQuoteClientInput, GenerateMotivationalQuoteOutput, GenerateNumericQuestionClientInput, GenerateNumericQuestionOutput, GeneratePracticeSuggestionClientInput, GeneratePracticeSuggestionOutput, GenerateQuestionsFromQuizPlanClientInput, GenerateQuestionsFromQuizPlanOutput, GenerateQuizFromTextClientInput, GenerateQuizFromTextOutput, GenerateQuizPlanClientInput, GenerateQuizPlanOutput, GenerateQuizReviewClientInput, GenerateQuizReviewOutput, GenerateSequenceQuestionClientInput, GenerateSequenceQuestionOutput, GenerateShortAnswerQuestionClientInput, GenerateShortAnswerQuestionOutput, GenerateSingleKnowledgeCardClientInput, GenerateSingleKnowledgeCardOutput, GenerateTrueFalseQuestionClientInput, GenerateTrueFalseQuestionOutput, PlanKnowledgeCardsClientInput, PlanKnowledgeCardsOutput, PlannedQuestion, assessAndMapDocument, generateCodingQuestion, generateFillInTheBlanksQuestion, generateLearningAnalysis, generateMCQQuestion, generateMRQQuestion, generateMatchingQuestion, generateMotivationalQuote, generateNumericQuestion, generatePracticeSuggestion, generateQuestionsFromQuizPlan, generateQuizFromText, generateQuizPlan, generateQuizReview, generateSequenceQuestion, generateShortAnswerQuestion, generateSingleKnowledgeCard, generateTrueFalseQuestion, planKnowledgeCards } from './ai.js';
5
- export { a as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, b as APIKeyManagerModal, c as ApiKeySettings, j as ApproachManager, B as BloomLevelManager, C as CategoryManager, i as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, M as MetadataTabs, e as QuestionFilters, f as QuestionFormDialog, d as QuestionList, h as QuestionTypeManager, Q as QuizAuthoringTool, S as SCORMExportModal, g as SubjectManager, k as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-Z0Qz6kch.js';
5
+ export { a as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, b as APIKeyManagerModal, c as ApiKeySettings, j as ApproachManager, B as BloomLevelManager, C as CategoryManager, i as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, M as MetadataTabs, e as QuestionFilters, f as QuestionFormDialog, d as QuestionList, h as QuestionTypeManager, Q as QuizAuthoringTool, S as SCORMExportModal, g as SubjectManager, k as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-ehLC4yr7.js';
6
6
  import 'clsx';
7
7
  import 'zod';
8
8
  import 'react';
@@ -4051,6 +4051,14 @@ var PracticeHistoryService = class {
4051
4051
  }
4052
4052
  static getPracticeStats() {
4053
4053
  const history2 = this.getPracticeHistory();
4054
+ return this.calculateStatsFromHistory(history2);
4055
+ }
4056
+ static clearHistory() {
4057
+ if (typeof window !== "undefined") {
4058
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
4059
+ }
4060
+ }
4061
+ static calculateStatsFromHistory(history2) {
4054
4062
  if (history2.length === 0) {
4055
4063
  return {
4056
4064
  totalSessions: 0,
@@ -4159,11 +4167,6 @@ var PracticeHistoryService = class {
4159
4167
  performanceByTopic: formatPerf(topicPerf)
4160
4168
  };
4161
4169
  }
4162
- static clearHistory() {
4163
- if (typeof window !== "undefined") {
4164
- localStorage.removeItem(LOCAL_STORAGE_KEY);
4165
- }
4166
- }
4167
4170
  };
4168
4171
  // NEW: A static property to hold the injected sync provider
4169
4172
  PracticeHistoryService.syncProvider = null;
@@ -105512,7 +105515,13 @@ init_react_shim();
105512
105515
 
105513
105516
  // src/react-ui/components/metadata/SubjectManager.tsx
105514
105517
  init_react_shim();
105515
- function SubjectManager() {
105518
+ function SubjectManager({
105519
+ initialData,
105520
+ isLoading: isLoadingProp,
105521
+ onAdd,
105522
+ onUpdate,
105523
+ onDelete
105524
+ }) {
105516
105525
  const [subjects, setSubjects] = useState([]);
105517
105526
  const [isLoading, setIsLoading] = useState(true);
105518
105527
  const [isDialogOpen, setIsDialogOpen] = useState(false);
@@ -105523,20 +105532,27 @@ function SubjectManager() {
105523
105532
  const [itemToDelete, setItemToDelete] = useState(null);
105524
105533
  const [isPending, startTransition] = useTransition();
105525
105534
  const { toast: toast2 } = useToast();
105526
- useEffect(() => {
105527
- setIsLoading(true);
105528
- try {
105529
- const data = MetadataService.getSubjects();
105530
- setSubjects(data);
105531
- } catch (error) {
105532
- toast2({ title: "Error", description: "Failed to fetch subjects from local storage.", variant: "destructive" });
105533
- } finally {
105534
- setIsLoading(false);
105535
- }
105536
- }, []);
105535
+ const isControlled = initialData !== void 0;
105537
105536
  const refreshData = () => {
105538
- setSubjects(MetadataService.getSubjects());
105537
+ if (!isControlled) {
105538
+ setIsLoading(true);
105539
+ try {
105540
+ setSubjects(MetadataService.getSubjects());
105541
+ } catch (error) {
105542
+ toast2({ title: "Error", description: "Failed to refresh subjects.", variant: "destructive" });
105543
+ } finally {
105544
+ setIsLoading(false);
105545
+ }
105546
+ }
105539
105547
  };
105548
+ useEffect(() => {
105549
+ if (isControlled) {
105550
+ setSubjects(initialData || []);
105551
+ setIsLoading(isLoadingProp || false);
105552
+ } else {
105553
+ refreshData();
105554
+ }
105555
+ }, [isControlled, initialData, isLoadingProp]);
105540
105556
  const handleAddItem = () => {
105541
105557
  setCurrentSubject(null);
105542
105558
  setSubjectName("");
@@ -105555,11 +105571,15 @@ function SubjectManager() {
105555
105571
  };
105556
105572
  const confirmDelete = () => {
105557
105573
  if (!itemToDelete) return;
105558
- startTransition(() => {
105574
+ startTransition(async () => {
105559
105575
  try {
105560
- MetadataService.deleteSubject(itemToDelete.code);
105576
+ if (isControlled && onDelete) {
105577
+ await onDelete(itemToDelete);
105578
+ } else {
105579
+ MetadataService.deleteSubject(itemToDelete.code);
105580
+ refreshData();
105581
+ }
105561
105582
  toast2({ title: "Success", description: `Subject "${itemToDelete.name}" deleted.` });
105562
- refreshData();
105563
105583
  } catch (error) {
105564
105584
  toast2({ title: "Error", description: error.message || "Failed to delete subject.", variant: "destructive" });
105565
105585
  } finally {
@@ -105573,40 +105593,32 @@ function SubjectManager() {
105573
105593
  toast2({ title: "Validation Error", description: "Please enter Subject Name and Subject Code.", variant: "destructive" });
105574
105594
  return;
105575
105595
  }
105576
- startTransition(() => {
105596
+ startTransition(async () => {
105577
105597
  try {
105578
105598
  if (currentSubject) {
105579
- MetadataService.updateSubject(currentSubject.id, subjectName, subjectCode);
105599
+ if (isControlled && onUpdate) {
105600
+ await onUpdate({ id: currentSubject.id, name: subjectName, code: subjectCode });
105601
+ } else {
105602
+ MetadataService.updateSubject(currentSubject.id, subjectName, subjectCode);
105603
+ refreshData();
105604
+ }
105580
105605
  toast2({ title: "Success", description: "Subject updated." });
105581
105606
  } else {
105582
- MetadataService.addSubject(subjectName, subjectCode);
105607
+ if (isControlled && onAdd) {
105608
+ await onAdd({ name: subjectName, code: subjectCode });
105609
+ } else {
105610
+ MetadataService.addSubject(subjectName, subjectCode);
105611
+ refreshData();
105612
+ }
105583
105613
  toast2({ title: "Success", description: "Subject added." });
105584
105614
  }
105585
- refreshData();
105586
105615
  setIsDialogOpen(false);
105587
105616
  } catch (error) {
105588
105617
  toast2({ title: "Error", description: error.message || "Failed to save subject.", variant: "destructive" });
105589
105618
  }
105590
105619
  });
105591
105620
  };
105592
- return /* @__PURE__ */ React97__default.createElement(Card, null, /* @__PURE__ */ React97__default.createElement(CardHeader, null, /* @__PURE__ */ React97__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React97__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React97__default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React97__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React97__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React97__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React97__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React97__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React97__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React97__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React97__default.createElement(Table2, null, /* @__PURE__ */ React97__default.createElement(TableHeader, null, /* @__PURE__ */ React97__default.createElement(TableRow, null, /* @__PURE__ */ React97__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React97__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React97__default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React97__default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React97__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React97__default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React97__default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React97__default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React97__default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React97__default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React97__default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React97__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React97__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React97__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React97__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React97__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React97__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React97__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React97__default.createElement(DialogHeader, null, /* @__PURE__ */ React97__default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React97__default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React97__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React97__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React97__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React97__default.createElement(
105593
- Input,
105594
- {
105595
- id: "subjectCode",
105596
- value: subjectCode,
105597
- onChange: (e2) => setSubjectCode(e2.target.value.toUpperCase()),
105598
- placeholder: "e.g., MATH",
105599
- disabled: !!currentSubject
105600
- }
105601
- )), /* @__PURE__ */ React97__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React97__default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React97__default.createElement(
105602
- Input,
105603
- {
105604
- id: "subjectName",
105605
- value: subjectName,
105606
- onChange: (e2) => setSubjectName(e2.target.value),
105607
- placeholder: "e.g., Mathematics"
105608
- }
105609
- ))), /* @__PURE__ */ React97__default.createElement(DialogFooter, null, /* @__PURE__ */ React97__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React97__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React97__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Save")))), /* @__PURE__ */ React97__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React97__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React97__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React97__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React97__default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '" (Code: ', itemToDelete?.code, "). Ensure no topics, questions, or learning objectives reference this subject.")), /* @__PURE__ */ React97__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React97__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React97__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React97__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Delete"))))));
105621
+ return /* @__PURE__ */ React97__default.createElement(Card, null, /* @__PURE__ */ React97__default.createElement(CardHeader, null, /* @__PURE__ */ React97__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React97__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React97__default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React97__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React97__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React97__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React97__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React97__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React97__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React97__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React97__default.createElement(Table2, null, /* @__PURE__ */ React97__default.createElement(TableHeader, null, /* @__PURE__ */ React97__default.createElement(TableRow, null, /* @__PURE__ */ React97__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React97__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React97__default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React97__default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React97__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React97__default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React97__default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React97__default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React97__default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React97__default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React97__default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React97__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React97__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React97__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React97__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React97__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React97__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React97__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React97__default.createElement(DialogHeader, null, /* @__PURE__ */ React97__default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React97__default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React97__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React97__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React97__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React97__default.createElement(Input, { id: "subjectCode", value: subjectCode, onChange: (e2) => setSubjectCode(e2.target.value.toUpperCase()), placeholder: "e.g., MATH", disabled: !!currentSubject })), /* @__PURE__ */ React97__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React97__default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React97__default.createElement(Input, { id: "subjectName", value: subjectName, onChange: (e2) => setSubjectName(e2.target.value), placeholder: "e.g., Mathematics" }))), /* @__PURE__ */ React97__default.createElement(DialogFooter, null, /* @__PURE__ */ React97__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React97__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React97__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React97__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React97__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React97__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React97__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React97__default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '".')), /* @__PURE__ */ React97__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React97__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React97__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React97__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
105610
105622
  }
105611
105623
 
105612
105624
  // src/react-ui/components/metadata/GradeLevelManager.tsx
package/dist/index.cjs CHANGED
@@ -2043,6 +2043,14 @@ var PracticeHistoryService = class {
2043
2043
  }
2044
2044
  static getPracticeStats() {
2045
2045
  const history = this.getPracticeHistory();
2046
+ return this.calculateStatsFromHistory(history);
2047
+ }
2048
+ static clearHistory() {
2049
+ if (typeof window !== "undefined") {
2050
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
2051
+ }
2052
+ }
2053
+ static calculateStatsFromHistory(history) {
2046
2054
  if (history.length === 0) {
2047
2055
  return {
2048
2056
  totalSessions: 0,
@@ -2151,11 +2159,6 @@ var PracticeHistoryService = class {
2151
2159
  performanceByTopic: formatPerf(topicPerf)
2152
2160
  };
2153
2161
  }
2154
- static clearHistory() {
2155
- if (typeof window !== "undefined") {
2156
- localStorage.removeItem(LOCAL_STORAGE_KEY);
2157
- }
2158
- }
2159
2162
  };
2160
2163
  // NEW: A static property to hold the injected sync provider
2161
2164
  PracticeHistoryService.syncProvider = null;
package/dist/index.d.cts CHANGED
@@ -246,6 +246,7 @@ declare class PracticeHistoryService {
246
246
  static getPracticeHistorySummary(): PracticeSessionSummary[];
247
247
  static getPracticeStats(): PracticeStats;
248
248
  static clearHistory(): void;
249
+ static calculateStatsFromHistory(history: PracticeSession[]): PracticeStats;
249
250
  }
250
251
 
251
252
  declare class AchievementService {
package/dist/index.d.ts CHANGED
@@ -246,6 +246,7 @@ declare class PracticeHistoryService {
246
246
  static getPracticeHistorySummary(): PracticeSessionSummary[];
247
247
  static getPracticeStats(): PracticeStats;
248
248
  static clearHistory(): void;
249
+ static calculateStatsFromHistory(history: PracticeSession[]): PracticeStats;
249
250
  }
250
251
 
251
252
  declare class AchievementService {
package/dist/index.mjs CHANGED
@@ -2037,6 +2037,14 @@ var PracticeHistoryService = class {
2037
2037
  }
2038
2038
  static getPracticeStats() {
2039
2039
  const history = this.getPracticeHistory();
2040
+ return this.calculateStatsFromHistory(history);
2041
+ }
2042
+ static clearHistory() {
2043
+ if (typeof window !== "undefined") {
2044
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
2045
+ }
2046
+ }
2047
+ static calculateStatsFromHistory(history) {
2040
2048
  if (history.length === 0) {
2041
2049
  return {
2042
2050
  totalSessions: 0,
@@ -2145,11 +2153,6 @@ var PracticeHistoryService = class {
2145
2153
  performanceByTopic: formatPerf(topicPerf)
2146
2154
  };
2147
2155
  }
2148
- static clearHistory() {
2149
- if (typeof window !== "undefined") {
2150
- localStorage.removeItem(LOCAL_STORAGE_KEY);
2151
- }
2152
- }
2153
2156
  };
2154
2157
  // NEW: A static property to hold the injected sync provider
2155
2158
  PracticeHistoryService.syncProvider = null;
package/dist/react-ui.cjs CHANGED
@@ -139667,6 +139667,14 @@ var PracticeHistoryService = class {
139667
139667
  }
139668
139668
  static getPracticeStats() {
139669
139669
  const history2 = this.getPracticeHistory();
139670
+ return this.calculateStatsFromHistory(history2);
139671
+ }
139672
+ static clearHistory() {
139673
+ if (typeof window !== "undefined") {
139674
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
139675
+ }
139676
+ }
139677
+ static calculateStatsFromHistory(history2) {
139670
139678
  if (history2.length === 0) {
139671
139679
  return {
139672
139680
  totalSessions: 0,
@@ -139775,11 +139783,6 @@ var PracticeHistoryService = class {
139775
139783
  performanceByTopic: formatPerf(topicPerf)
139776
139784
  };
139777
139785
  }
139778
- static clearHistory() {
139779
- if (typeof window !== "undefined") {
139780
- localStorage.removeItem(LOCAL_STORAGE_KEY);
139781
- }
139782
- }
139783
139786
  };
139784
139787
  // NEW: A static property to hold the injected sync provider
139785
139788
  PracticeHistoryService.syncProvider = null;
@@ -168136,7 +168139,13 @@ init_react_shim();
168136
168139
 
168137
168140
  // src/react-ui/components/metadata/SubjectManager.tsx
168138
168141
  init_react_shim();
168139
- function SubjectManager() {
168142
+ function SubjectManager({
168143
+ initialData,
168144
+ isLoading: isLoadingProp,
168145
+ onAdd,
168146
+ onUpdate,
168147
+ onDelete
168148
+ }) {
168140
168149
  const [subjects, setSubjects] = React163.useState([]);
168141
168150
  const [isLoading, setIsLoading] = React163.useState(true);
168142
168151
  const [isDialogOpen, setIsDialogOpen] = React163.useState(false);
@@ -168147,20 +168156,27 @@ function SubjectManager() {
168147
168156
  const [itemToDelete, setItemToDelete] = React163.useState(null);
168148
168157
  const [isPending, startTransition] = React163.useTransition();
168149
168158
  const { toast: toast2 } = useToast();
168150
- React163.useEffect(() => {
168151
- setIsLoading(true);
168152
- try {
168153
- const data = MetadataService.getSubjects();
168154
- setSubjects(data);
168155
- } catch (error) {
168156
- toast2({ title: "Error", description: "Failed to fetch subjects from local storage.", variant: "destructive" });
168157
- } finally {
168158
- setIsLoading(false);
168159
- }
168160
- }, []);
168159
+ const isControlled = initialData !== void 0;
168161
168160
  const refreshData = () => {
168162
- setSubjects(MetadataService.getSubjects());
168161
+ if (!isControlled) {
168162
+ setIsLoading(true);
168163
+ try {
168164
+ setSubjects(MetadataService.getSubjects());
168165
+ } catch (error) {
168166
+ toast2({ title: "Error", description: "Failed to refresh subjects.", variant: "destructive" });
168167
+ } finally {
168168
+ setIsLoading(false);
168169
+ }
168170
+ }
168163
168171
  };
168172
+ React163.useEffect(() => {
168173
+ if (isControlled) {
168174
+ setSubjects(initialData || []);
168175
+ setIsLoading(isLoadingProp || false);
168176
+ } else {
168177
+ refreshData();
168178
+ }
168179
+ }, [isControlled, initialData, isLoadingProp]);
168164
168180
  const handleAddItem = () => {
168165
168181
  setCurrentSubject(null);
168166
168182
  setSubjectName("");
@@ -168179,11 +168195,15 @@ function SubjectManager() {
168179
168195
  };
168180
168196
  const confirmDelete = () => {
168181
168197
  if (!itemToDelete) return;
168182
- startTransition(() => {
168198
+ startTransition(async () => {
168183
168199
  try {
168184
- MetadataService.deleteSubject(itemToDelete.code);
168200
+ if (isControlled && onDelete) {
168201
+ await onDelete(itemToDelete);
168202
+ } else {
168203
+ MetadataService.deleteSubject(itemToDelete.code);
168204
+ refreshData();
168205
+ }
168185
168206
  toast2({ title: "Success", description: `Subject "${itemToDelete.name}" deleted.` });
168186
- refreshData();
168187
168207
  } catch (error) {
168188
168208
  toast2({ title: "Error", description: error.message || "Failed to delete subject.", variant: "destructive" });
168189
168209
  } finally {
@@ -168197,40 +168217,32 @@ function SubjectManager() {
168197
168217
  toast2({ title: "Validation Error", description: "Please enter Subject Name and Subject Code.", variant: "destructive" });
168198
168218
  return;
168199
168219
  }
168200
- startTransition(() => {
168220
+ startTransition(async () => {
168201
168221
  try {
168202
168222
  if (currentSubject) {
168203
- MetadataService.updateSubject(currentSubject.id, subjectName, subjectCode);
168223
+ if (isControlled && onUpdate) {
168224
+ await onUpdate({ id: currentSubject.id, name: subjectName, code: subjectCode });
168225
+ } else {
168226
+ MetadataService.updateSubject(currentSubject.id, subjectName, subjectCode);
168227
+ refreshData();
168228
+ }
168204
168229
  toast2({ title: "Success", description: "Subject updated." });
168205
168230
  } else {
168206
- MetadataService.addSubject(subjectName, subjectCode);
168231
+ if (isControlled && onAdd) {
168232
+ await onAdd({ name: subjectName, code: subjectCode });
168233
+ } else {
168234
+ MetadataService.addSubject(subjectName, subjectCode);
168235
+ refreshData();
168236
+ }
168207
168237
  toast2({ title: "Success", description: "Subject added." });
168208
168238
  }
168209
- refreshData();
168210
168239
  setIsDialogOpen(false);
168211
168240
  } catch (error) {
168212
168241
  toast2({ title: "Error", description: error.message || "Failed to save subject.", variant: "destructive" });
168213
168242
  }
168214
168243
  });
168215
168244
  };
168216
- return /* @__PURE__ */ React163__namespace.default.createElement(Card, null, /* @__PURE__ */ React163__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React163__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React163__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React163__namespace.default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React163__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React163__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React163__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React163__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React163__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React163__namespace.default.createElement(Table3, null, /* @__PURE__ */ React163__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React163__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React163__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React163__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React163__namespace.default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React163__namespace.default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React163__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React163__namespace.default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React163__namespace.default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React163__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React163__namespace.default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React163__namespace.default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React163__namespace.default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React163__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React163__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React163__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React163__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React163__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React163__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React163__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React163__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React163__namespace.default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React163__namespace.default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React163__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React163__namespace.default.createElement(
168217
- Input,
168218
- {
168219
- id: "subjectCode",
168220
- value: subjectCode,
168221
- onChange: (e3) => setSubjectCode(e3.target.value.toUpperCase()),
168222
- placeholder: "e.g., MATH",
168223
- disabled: !!currentSubject
168224
- }
168225
- )), /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React163__namespace.default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React163__namespace.default.createElement(
168226
- Input,
168227
- {
168228
- id: "subjectName",
168229
- value: subjectName,
168230
- onChange: (e3) => setSubjectName(e3.target.value),
168231
- placeholder: "e.g., Mathematics"
168232
- }
168233
- ))), /* @__PURE__ */ React163__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React163__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React163__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React163__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Save")))), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '" (Code: ', itemToDelete?.code, "). Ensure no topics, questions, or learning objectives reference this subject.")), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React163__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Delete"))))));
168245
+ return /* @__PURE__ */ React163__namespace.default.createElement(Card, null, /* @__PURE__ */ React163__namespace.default.createElement(CardHeader, null, /* @__PURE__ */ React163__namespace.default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React163__namespace.default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React163__namespace.default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React163__namespace.default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React163__namespace.default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React163__namespace.default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React163__namespace.default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React163__namespace.default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React163__namespace.default.createElement(Table3, null, /* @__PURE__ */ React163__namespace.default.createElement(TableHeader, null, /* @__PURE__ */ React163__namespace.default.createElement(TableRow, null, /* @__PURE__ */ React163__namespace.default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React163__namespace.default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React163__namespace.default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React163__namespace.default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React163__namespace.default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React163__namespace.default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React163__namespace.default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React163__namespace.default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React163__namespace.default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React163__namespace.default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React163__namespace.default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React163__namespace.default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React163__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React163__namespace.default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React163__namespace.default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React163__namespace.default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React163__namespace.default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React163__namespace.default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React163__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React163__namespace.default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React163__namespace.default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React163__namespace.default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React163__namespace.default.createElement(Input, { id: "subjectCode", value: subjectCode, onChange: (e3) => setSubjectCode(e3.target.value.toUpperCase()), placeholder: "e.g., MATH", disabled: !!currentSubject })), /* @__PURE__ */ React163__namespace.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React163__namespace.default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React163__namespace.default.createElement(Input, { id: "subjectName", value: subjectName, onChange: (e3) => setSubjectName(e3.target.value), placeholder: "e.g., Mathematics" }))), /* @__PURE__ */ React163__namespace.default.createElement(DialogFooter, null, /* @__PURE__ */ React163__namespace.default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React163__namespace.default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React163__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '".')), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React163__namespace.default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React163__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
168234
168246
  }
168235
168247
 
168236
168248
  // src/react-ui/components/metadata/GradeLevelManager.tsx
@@ -5,7 +5,7 @@ import { Q as QuizResultType, U as UserAnswerType, n as PracticeSession, o as Pr
5
5
  export { j as AchievementDefinition, r as ActivityCalendarData, u as AnalysisReport, A as AnswerDetail, x as ChatContext, C as ChatMessage, v as DashboardCardConfig, D as DashboardCardId, w as DashboardLayout, y as Goal, G as GoalType, t as ImageContextItem, I as ImportError, K as KnowledgeCard, L as LearningAnalysis, e as PerformanceByBloomLevel, b as PerformanceByCategory, d as PerformanceByDifficulty, P as PerformanceByLearningObjective, c as PerformanceByTopic, f as PerformanceMetric, s as PerformanceSummary, k as PracticeDifficulty, q as PracticeTopicSummary, g as QuestionReview, R as RoadmapItem, T as TestCaseResult, a as UserAnswers, W as WeeklyRoadmap } from './ai-ecosystem-DqFRlFU3.cjs';
6
6
  import * as React$1 from 'react';
7
7
  import React__default, { ReactNode } from 'react';
8
- export { a as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, b as APIKeyManagerModal, c as ApiKeySettings, j as ApproachManager, B as BloomLevelManager, C as CategoryManager, i as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, M as MetadataTabs, e as QuestionFilters, f as QuestionFormDialog, d as QuestionList, h as QuestionTypeManager, Q as QuizAuthoringTool, S as SCORMExportModal, g as SubjectManager, k as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-DfLqsPwa.cjs';
8
+ export { a as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, b as APIKeyManagerModal, c as ApiKeySettings, j as ApproachManager, B as BloomLevelManager, C as CategoryManager, i as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, M as MetadataTabs, e as QuestionFilters, f as QuestionFormDialog, d as QuestionList, h as QuestionTypeManager, Q as QuizAuthoringTool, S as SCORMExportModal, g as SubjectManager, k as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-UW4hDEhn.cjs';
9
9
  import * as class_variance_authority_types from 'class-variance-authority/types';
10
10
  import { VariantProps } from 'class-variance-authority';
11
11
  import * as LabelPrimitive from '@radix-ui/react-label';
@@ -5,7 +5,7 @@ import { Q as QuizResultType, U as UserAnswerType, n as PracticeSession, o as Pr
5
5
  export { j as AchievementDefinition, r as ActivityCalendarData, u as AnalysisReport, A as AnswerDetail, x as ChatContext, C as ChatMessage, v as DashboardCardConfig, D as DashboardCardId, w as DashboardLayout, y as Goal, G as GoalType, t as ImageContextItem, I as ImportError, K as KnowledgeCard, L as LearningAnalysis, e as PerformanceByBloomLevel, b as PerformanceByCategory, d as PerformanceByDifficulty, P as PerformanceByLearningObjective, c as PerformanceByTopic, f as PerformanceMetric, s as PerformanceSummary, k as PracticeDifficulty, q as PracticeTopicSummary, g as QuestionReview, R as RoadmapItem, T as TestCaseResult, a as UserAnswers, W as WeeklyRoadmap } from './ai-ecosystem-DqVlSO3r.js';
6
6
  import * as React$1 from 'react';
7
7
  import React__default, { ReactNode } from 'react';
8
- export { a as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, b as APIKeyManagerModal, c as ApiKeySettings, j as ApproachManager, B as BloomLevelManager, C as CategoryManager, i as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, M as MetadataTabs, e as QuestionFilters, f as QuestionFormDialog, d as QuestionList, h as QuestionTypeManager, Q as QuizAuthoringTool, S as SCORMExportModal, g as SubjectManager, k as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-Z0Qz6kch.js';
8
+ export { a as AIFullQuizGeneratorModal, A as AIQuestionGeneratorModal, b as APIKeyManagerModal, c as ApiKeySettings, j as ApproachManager, B as BloomLevelManager, C as CategoryManager, i as ContextManager, E as EditQuestionModal, G as GradeLevelManager, I as ImportQuestionsModal, L as LearningObjectiveManager, M as MetadataTabs, e as QuestionFilters, f as QuestionFormDialog, d as QuestionList, h as QuestionTypeManager, Q as QuizAuthoringTool, S as SCORMExportModal, g as SubjectManager, k as Toaster, T as TopicManager, t as toast, u as useToast } from './toaster-ehLC4yr7.js';
9
9
  import * as class_variance_authority_types from 'class-variance-authority/types';
10
10
  import { VariantProps } from 'class-variance-authority';
11
11
  import * as LabelPrimitive from '@radix-ui/react-label';
package/dist/react-ui.mjs CHANGED
@@ -139640,6 +139640,14 @@ var PracticeHistoryService = class {
139640
139640
  }
139641
139641
  static getPracticeStats() {
139642
139642
  const history2 = this.getPracticeHistory();
139643
+ return this.calculateStatsFromHistory(history2);
139644
+ }
139645
+ static clearHistory() {
139646
+ if (typeof window !== "undefined") {
139647
+ localStorage.removeItem(LOCAL_STORAGE_KEY);
139648
+ }
139649
+ }
139650
+ static calculateStatsFromHistory(history2) {
139643
139651
  if (history2.length === 0) {
139644
139652
  return {
139645
139653
  totalSessions: 0,
@@ -139748,11 +139756,6 @@ var PracticeHistoryService = class {
139748
139756
  performanceByTopic: formatPerf(topicPerf)
139749
139757
  };
139750
139758
  }
139751
- static clearHistory() {
139752
- if (typeof window !== "undefined") {
139753
- localStorage.removeItem(LOCAL_STORAGE_KEY);
139754
- }
139755
- }
139756
139759
  };
139757
139760
  // NEW: A static property to hold the injected sync provider
139758
139761
  PracticeHistoryService.syncProvider = null;
@@ -168109,7 +168112,13 @@ init_react_shim();
168109
168112
 
168110
168113
  // src/react-ui/components/metadata/SubjectManager.tsx
168111
168114
  init_react_shim();
168112
- function SubjectManager() {
168115
+ function SubjectManager({
168116
+ initialData,
168117
+ isLoading: isLoadingProp,
168118
+ onAdd,
168119
+ onUpdate,
168120
+ onDelete
168121
+ }) {
168113
168122
  const [subjects, setSubjects] = useState([]);
168114
168123
  const [isLoading, setIsLoading] = useState(true);
168115
168124
  const [isDialogOpen, setIsDialogOpen] = useState(false);
@@ -168120,20 +168129,27 @@ function SubjectManager() {
168120
168129
  const [itemToDelete, setItemToDelete] = useState(null);
168121
168130
  const [isPending, startTransition] = useTransition();
168122
168131
  const { toast: toast2 } = useToast();
168123
- useEffect(() => {
168124
- setIsLoading(true);
168125
- try {
168126
- const data = MetadataService.getSubjects();
168127
- setSubjects(data);
168128
- } catch (error) {
168129
- toast2({ title: "Error", description: "Failed to fetch subjects from local storage.", variant: "destructive" });
168130
- } finally {
168131
- setIsLoading(false);
168132
- }
168133
- }, []);
168132
+ const isControlled = initialData !== void 0;
168134
168133
  const refreshData = () => {
168135
- setSubjects(MetadataService.getSubjects());
168134
+ if (!isControlled) {
168135
+ setIsLoading(true);
168136
+ try {
168137
+ setSubjects(MetadataService.getSubjects());
168138
+ } catch (error) {
168139
+ toast2({ title: "Error", description: "Failed to refresh subjects.", variant: "destructive" });
168140
+ } finally {
168141
+ setIsLoading(false);
168142
+ }
168143
+ }
168136
168144
  };
168145
+ useEffect(() => {
168146
+ if (isControlled) {
168147
+ setSubjects(initialData || []);
168148
+ setIsLoading(isLoadingProp || false);
168149
+ } else {
168150
+ refreshData();
168151
+ }
168152
+ }, [isControlled, initialData, isLoadingProp]);
168137
168153
  const handleAddItem = () => {
168138
168154
  setCurrentSubject(null);
168139
168155
  setSubjectName("");
@@ -168152,11 +168168,15 @@ function SubjectManager() {
168152
168168
  };
168153
168169
  const confirmDelete = () => {
168154
168170
  if (!itemToDelete) return;
168155
- startTransition(() => {
168171
+ startTransition(async () => {
168156
168172
  try {
168157
- MetadataService.deleteSubject(itemToDelete.code);
168173
+ if (isControlled && onDelete) {
168174
+ await onDelete(itemToDelete);
168175
+ } else {
168176
+ MetadataService.deleteSubject(itemToDelete.code);
168177
+ refreshData();
168178
+ }
168158
168179
  toast2({ title: "Success", description: `Subject "${itemToDelete.name}" deleted.` });
168159
- refreshData();
168160
168180
  } catch (error) {
168161
168181
  toast2({ title: "Error", description: error.message || "Failed to delete subject.", variant: "destructive" });
168162
168182
  } finally {
@@ -168170,40 +168190,32 @@ function SubjectManager() {
168170
168190
  toast2({ title: "Validation Error", description: "Please enter Subject Name and Subject Code.", variant: "destructive" });
168171
168191
  return;
168172
168192
  }
168173
- startTransition(() => {
168193
+ startTransition(async () => {
168174
168194
  try {
168175
168195
  if (currentSubject) {
168176
- MetadataService.updateSubject(currentSubject.id, subjectName, subjectCode);
168196
+ if (isControlled && onUpdate) {
168197
+ await onUpdate({ id: currentSubject.id, name: subjectName, code: subjectCode });
168198
+ } else {
168199
+ MetadataService.updateSubject(currentSubject.id, subjectName, subjectCode);
168200
+ refreshData();
168201
+ }
168177
168202
  toast2({ title: "Success", description: "Subject updated." });
168178
168203
  } else {
168179
- MetadataService.addSubject(subjectName, subjectCode);
168204
+ if (isControlled && onAdd) {
168205
+ await onAdd({ name: subjectName, code: subjectCode });
168206
+ } else {
168207
+ MetadataService.addSubject(subjectName, subjectCode);
168208
+ refreshData();
168209
+ }
168180
168210
  toast2({ title: "Success", description: "Subject added." });
168181
168211
  }
168182
- refreshData();
168183
168212
  setIsDialogOpen(false);
168184
168213
  } catch (error) {
168185
168214
  toast2({ title: "Error", description: error.message || "Failed to save subject.", variant: "destructive" });
168186
168215
  }
168187
168216
  });
168188
168217
  };
168189
- return /* @__PURE__ */ React163__default.createElement(Card, null, /* @__PURE__ */ React163__default.createElement(CardHeader, null, /* @__PURE__ */ React163__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React163__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React163__default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React163__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React163__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React163__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React163__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React163__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React163__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React163__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React163__default.createElement(Table3, null, /* @__PURE__ */ React163__default.createElement(TableHeader, null, /* @__PURE__ */ React163__default.createElement(TableRow, null, /* @__PURE__ */ React163__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React163__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React163__default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React163__default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React163__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React163__default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React163__default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React163__default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React163__default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React163__default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React163__default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React163__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React163__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React163__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React163__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React163__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React163__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React163__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React163__default.createElement(DialogHeader, null, /* @__PURE__ */ React163__default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React163__default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React163__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React163__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React163__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React163__default.createElement(
168190
- Input,
168191
- {
168192
- id: "subjectCode",
168193
- value: subjectCode,
168194
- onChange: (e3) => setSubjectCode(e3.target.value.toUpperCase()),
168195
- placeholder: "e.g., MATH",
168196
- disabled: !!currentSubject
168197
- }
168198
- )), /* @__PURE__ */ React163__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React163__default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React163__default.createElement(
168199
- Input,
168200
- {
168201
- id: "subjectName",
168202
- value: subjectName,
168203
- onChange: (e3) => setSubjectName(e3.target.value),
168204
- placeholder: "e.g., Mathematics"
168205
- }
168206
- ))), /* @__PURE__ */ React163__default.createElement(DialogFooter, null, /* @__PURE__ */ React163__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React163__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React163__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Save")))), /* @__PURE__ */ React163__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React163__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React163__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React163__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React163__default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '" (Code: ', itemToDelete?.code, "). Ensure no topics, questions, or learning objectives reference this subject.")), /* @__PURE__ */ React163__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React163__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React163__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React163__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), "Delete"))))));
168218
+ return /* @__PURE__ */ React163__default.createElement(Card, null, /* @__PURE__ */ React163__default.createElement(CardHeader, null, /* @__PURE__ */ React163__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React163__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React163__default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React163__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React163__default.createElement(CirclePlus, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React163__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React163__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React163__default.createElement(LoaderCircle, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React163__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React163__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React163__default.createElement(Table3, null, /* @__PURE__ */ React163__default.createElement(TableHeader, null, /* @__PURE__ */ React163__default.createElement(TableRow, null, /* @__PURE__ */ React163__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React163__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React163__default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React163__default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React163__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React163__default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React163__default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React163__default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React163__default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React163__default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React163__default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React163__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React163__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React163__default.createElement(PenLine, { className: "h-4 w-4" })), /* @__PURE__ */ React163__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React163__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React163__default.createElement(Dialog2, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React163__default.createElement(DialogContent2, { className: "sm:max-w-md" }, /* @__PURE__ */ React163__default.createElement(DialogHeader, null, /* @__PURE__ */ React163__default.createElement(DialogTitle2, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React163__default.createElement(DialogDescription2, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React163__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React163__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React163__default.createElement(Label2, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React163__default.createElement(Input, { id: "subjectCode", value: subjectCode, onChange: (e3) => setSubjectCode(e3.target.value.toUpperCase()), placeholder: "e.g., MATH", disabled: !!currentSubject })), /* @__PURE__ */ React163__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React163__default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React163__default.createElement(Input, { id: "subjectName", value: subjectName, onChange: (e3) => setSubjectName(e3.target.value), placeholder: "e.g., Mathematics" }))), /* @__PURE__ */ React163__default.createElement(DialogFooter, null, /* @__PURE__ */ React163__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React163__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React163__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React163__default.createElement(AlertDialog2, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React163__default.createElement(AlertDialogContent2, null, /* @__PURE__ */ React163__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React163__default.createElement(AlertDialogTitle2, null, "Are you sure?"), /* @__PURE__ */ React163__default.createElement(AlertDialogDescription2, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete?.name, '".')), /* @__PURE__ */ React163__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React163__default.createElement(AlertDialogCancel2, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React163__default.createElement(AlertDialogAction2, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React163__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
168207
168219
  }
168208
168220
 
168209
168221
  // src/react-ui/components/metadata/GradeLevelManager.tsx
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import React__default from 'react';
3
3
  import { s as QuizConfig, g as QuizQuestion } from './quiz-config-o4j2dfsu.cjs';
4
- import { QuestionInBank, QuestionFilters as QuestionFilters$1 } from './index.cjs';
4
+ import { QuestionInBank, QuestionFilters as QuestionFilters$1, Subject } from './index.cjs';
5
5
  import * as class_variance_authority_types from 'class-variance-authority/types';
6
6
  import * as ToastPrimitives from '@radix-ui/react-toast';
7
7
  import { VariantProps } from 'class-variance-authority';
@@ -90,7 +90,31 @@ declare function ApiKeySettings(): React__default.JSX.Element;
90
90
 
91
91
  declare function MetadataTabs(): React__default.JSX.Element;
92
92
 
93
- declare function SubjectManager(): React__default.JSX.Element;
93
+ /**
94
+ * Props for the SubjectManager component.
95
+ * When these props are provided, the component operates in "controlled" mode.
96
+ * If they are omitted, it falls back to using its internal localStorage service.
97
+ */
98
+ interface SubjectManagerProps {
99
+ /** Optional: An array of subjects to display. If provided, the component will not fetch its own data. */
100
+ initialData?: Subject[];
101
+ /** Optional: A flag to indicate if the parent component is loading data. */
102
+ isLoading?: boolean;
103
+ /** Optional: An async callback function to handle adding a new subject. */
104
+ onAdd?: (item: {
105
+ name: string;
106
+ code: string;
107
+ }) => Promise<void>;
108
+ /** Optional: An async callback function to handle updating an existing subject. */
109
+ onUpdate?: (item: {
110
+ id: string;
111
+ name: string;
112
+ code: string;
113
+ }) => Promise<void>;
114
+ /** Optional: An async callback function to handle deleting a subject. */
115
+ onDelete?: (item: Subject) => Promise<void>;
116
+ }
117
+ declare function SubjectManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }: SubjectManagerProps): React__default.JSX.Element;
94
118
 
95
119
  declare function TopicManager(): React__default.JSX.Element;
96
120
 
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import React__default from 'react';
3
3
  import { s as QuizConfig, g as QuizQuestion } from './quiz-config-o4j2dfsu.js';
4
- import { QuestionInBank, QuestionFilters as QuestionFilters$1 } from './index.js';
4
+ import { QuestionInBank, QuestionFilters as QuestionFilters$1, Subject } from './index.js';
5
5
  import * as class_variance_authority_types from 'class-variance-authority/types';
6
6
  import * as ToastPrimitives from '@radix-ui/react-toast';
7
7
  import { VariantProps } from 'class-variance-authority';
@@ -90,7 +90,31 @@ declare function ApiKeySettings(): React__default.JSX.Element;
90
90
 
91
91
  declare function MetadataTabs(): React__default.JSX.Element;
92
92
 
93
- declare function SubjectManager(): React__default.JSX.Element;
93
+ /**
94
+ * Props for the SubjectManager component.
95
+ * When these props are provided, the component operates in "controlled" mode.
96
+ * If they are omitted, it falls back to using its internal localStorage service.
97
+ */
98
+ interface SubjectManagerProps {
99
+ /** Optional: An array of subjects to display. If provided, the component will not fetch its own data. */
100
+ initialData?: Subject[];
101
+ /** Optional: A flag to indicate if the parent component is loading data. */
102
+ isLoading?: boolean;
103
+ /** Optional: An async callback function to handle adding a new subject. */
104
+ onAdd?: (item: {
105
+ name: string;
106
+ code: string;
107
+ }) => Promise<void>;
108
+ /** Optional: An async callback function to handle updating an existing subject. */
109
+ onUpdate?: (item: {
110
+ id: string;
111
+ name: string;
112
+ code: string;
113
+ }) => Promise<void>;
114
+ /** Optional: An async callback function to handle deleting a subject. */
115
+ onDelete?: (item: Subject) => Promise<void>;
116
+ }
117
+ declare function SubjectManager({ initialData, isLoading: isLoadingProp, onAdd, onUpdate, onDelete }: SubjectManagerProps): React__default.JSX.Element;
94
118
 
95
119
  declare function TopicManager(): React__default.JSX.Element;
96
120
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thanh01.pmt/interactive-quiz-kit",
3
- "version": "1.0.48",
3
+ "version": "1.0.50",
4
4
  "description": "A comprehensive library for creating, managing, and playing interactive quizzes, with AI generation and SCORM support.",
5
5
  "keywords": [
6
6
  "react",