@thanh01.pmt/interactive-quiz-kit 1.0.49 → 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.
@@ -105541,7 +105541,13 @@ init_react_shim();
105541
105541
 
105542
105542
  // src/react-ui/components/metadata/SubjectManager.tsx
105543
105543
  init_react_shim();
105544
- function SubjectManager() {
105544
+ function SubjectManager({
105545
+ initialData,
105546
+ isLoading: isLoadingProp,
105547
+ onAdd,
105548
+ onUpdate,
105549
+ onDelete
105550
+ }) {
105545
105551
  const [subjects, setSubjects] = React97.useState([]);
105546
105552
  const [isLoading, setIsLoading] = React97.useState(true);
105547
105553
  const [isDialogOpen, setIsDialogOpen] = React97.useState(false);
@@ -105552,20 +105558,27 @@ function SubjectManager() {
105552
105558
  const [itemToDelete, setItemToDelete] = React97.useState(null);
105553
105559
  const [isPending, startTransition] = React97.useTransition();
105554
105560
  const { toast: toast2 } = useToast();
105555
- React97.useEffect(() => {
105556
- setIsLoading(true);
105557
- try {
105558
- const data = MetadataService.getSubjects();
105559
- setSubjects(data);
105560
- } catch (error) {
105561
- toast2({ title: "Error", description: "Failed to fetch subjects from local storage.", variant: "destructive" });
105562
- } finally {
105563
- setIsLoading(false);
105564
- }
105565
- }, []);
105561
+ const isControlled = initialData !== void 0;
105566
105562
  const refreshData = () => {
105567
- 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
+ }
105568
105573
  };
105574
+ React97.useEffect(() => {
105575
+ if (isControlled) {
105576
+ setSubjects(initialData || []);
105577
+ setIsLoading(isLoadingProp || false);
105578
+ } else {
105579
+ refreshData();
105580
+ }
105581
+ }, [isControlled, initialData, isLoadingProp]);
105569
105582
  const handleAddItem = () => {
105570
105583
  setCurrentSubject(null);
105571
105584
  setSubjectName("");
@@ -105584,11 +105597,15 @@ function SubjectManager() {
105584
105597
  };
105585
105598
  const confirmDelete = () => {
105586
105599
  if (!itemToDelete) return;
105587
- startTransition(() => {
105600
+ startTransition(async () => {
105588
105601
  try {
105589
- MetadataService.deleteSubject(itemToDelete.code);
105602
+ if (isControlled && onDelete) {
105603
+ await onDelete(itemToDelete);
105604
+ } else {
105605
+ MetadataService.deleteSubject(itemToDelete.code);
105606
+ refreshData();
105607
+ }
105590
105608
  toast2({ title: "Success", description: `Subject "${itemToDelete.name}" deleted.` });
105591
- refreshData();
105592
105609
  } catch (error) {
105593
105610
  toast2({ title: "Error", description: error.message || "Failed to delete subject.", variant: "destructive" });
105594
105611
  } finally {
@@ -105602,40 +105619,32 @@ function SubjectManager() {
105602
105619
  toast2({ title: "Validation Error", description: "Please enter Subject Name and Subject Code.", variant: "destructive" });
105603
105620
  return;
105604
105621
  }
105605
- startTransition(() => {
105622
+ startTransition(async () => {
105606
105623
  try {
105607
105624
  if (currentSubject) {
105608
- 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
+ }
105609
105631
  toast2({ title: "Success", description: "Subject updated." });
105610
105632
  } else {
105611
- 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
+ }
105612
105639
  toast2({ title: "Success", description: "Subject added." });
105613
105640
  }
105614
- refreshData();
105615
105641
  setIsDialogOpen(false);
105616
105642
  } catch (error) {
105617
105643
  toast2({ title: "Error", description: error.message || "Failed to save subject.", variant: "destructive" });
105618
105644
  }
105619
105645
  });
105620
105646
  };
105621
- 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(
105622
- Input,
105623
- {
105624
- id: "subjectCode",
105625
- value: subjectCode,
105626
- onChange: (e2) => setSubjectCode(e2.target.value.toUpperCase()),
105627
- placeholder: "e.g., MATH",
105628
- disabled: !!currentSubject
105629
- }
105630
- )), /* @__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(
105631
- Input,
105632
- {
105633
- id: "subjectName",
105634
- value: subjectName,
105635
- onChange: (e2) => setSubjectName(e2.target.value),
105636
- placeholder: "e.g., Mathematics"
105637
- }
105638
- ))), /* @__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"))))));
105639
105648
  }
105640
105649
 
105641
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';
@@ -105515,7 +105515,13 @@ init_react_shim();
105515
105515
 
105516
105516
  // src/react-ui/components/metadata/SubjectManager.tsx
105517
105517
  init_react_shim();
105518
- function SubjectManager() {
105518
+ function SubjectManager({
105519
+ initialData,
105520
+ isLoading: isLoadingProp,
105521
+ onAdd,
105522
+ onUpdate,
105523
+ onDelete
105524
+ }) {
105519
105525
  const [subjects, setSubjects] = useState([]);
105520
105526
  const [isLoading, setIsLoading] = useState(true);
105521
105527
  const [isDialogOpen, setIsDialogOpen] = useState(false);
@@ -105526,20 +105532,27 @@ function SubjectManager() {
105526
105532
  const [itemToDelete, setItemToDelete] = useState(null);
105527
105533
  const [isPending, startTransition] = useTransition();
105528
105534
  const { toast: toast2 } = useToast();
105529
- useEffect(() => {
105530
- setIsLoading(true);
105531
- try {
105532
- const data = MetadataService.getSubjects();
105533
- setSubjects(data);
105534
- } catch (error) {
105535
- toast2({ title: "Error", description: "Failed to fetch subjects from local storage.", variant: "destructive" });
105536
- } finally {
105537
- setIsLoading(false);
105538
- }
105539
- }, []);
105535
+ const isControlled = initialData !== void 0;
105540
105536
  const refreshData = () => {
105541
- 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
+ }
105542
105547
  };
105548
+ useEffect(() => {
105549
+ if (isControlled) {
105550
+ setSubjects(initialData || []);
105551
+ setIsLoading(isLoadingProp || false);
105552
+ } else {
105553
+ refreshData();
105554
+ }
105555
+ }, [isControlled, initialData, isLoadingProp]);
105543
105556
  const handleAddItem = () => {
105544
105557
  setCurrentSubject(null);
105545
105558
  setSubjectName("");
@@ -105558,11 +105571,15 @@ function SubjectManager() {
105558
105571
  };
105559
105572
  const confirmDelete = () => {
105560
105573
  if (!itemToDelete) return;
105561
- startTransition(() => {
105574
+ startTransition(async () => {
105562
105575
  try {
105563
- MetadataService.deleteSubject(itemToDelete.code);
105576
+ if (isControlled && onDelete) {
105577
+ await onDelete(itemToDelete);
105578
+ } else {
105579
+ MetadataService.deleteSubject(itemToDelete.code);
105580
+ refreshData();
105581
+ }
105564
105582
  toast2({ title: "Success", description: `Subject "${itemToDelete.name}" deleted.` });
105565
- refreshData();
105566
105583
  } catch (error) {
105567
105584
  toast2({ title: "Error", description: error.message || "Failed to delete subject.", variant: "destructive" });
105568
105585
  } finally {
@@ -105576,40 +105593,32 @@ function SubjectManager() {
105576
105593
  toast2({ title: "Validation Error", description: "Please enter Subject Name and Subject Code.", variant: "destructive" });
105577
105594
  return;
105578
105595
  }
105579
- startTransition(() => {
105596
+ startTransition(async () => {
105580
105597
  try {
105581
105598
  if (currentSubject) {
105582
- 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
+ }
105583
105605
  toast2({ title: "Success", description: "Subject updated." });
105584
105606
  } else {
105585
- 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
+ }
105586
105613
  toast2({ title: "Success", description: "Subject added." });
105587
105614
  }
105588
- refreshData();
105589
105615
  setIsDialogOpen(false);
105590
105616
  } catch (error) {
105591
105617
  toast2({ title: "Error", description: error.message || "Failed to save subject.", variant: "destructive" });
105592
105618
  }
105593
105619
  });
105594
105620
  };
105595
- 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(
105596
- Input,
105597
- {
105598
- id: "subjectCode",
105599
- value: subjectCode,
105600
- onChange: (e2) => setSubjectCode(e2.target.value.toUpperCase()),
105601
- placeholder: "e.g., MATH",
105602
- disabled: !!currentSubject
105603
- }
105604
- )), /* @__PURE__ */ React97__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React97__default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React97__default.createElement(
105605
- Input,
105606
- {
105607
- id: "subjectName",
105608
- value: subjectName,
105609
- onChange: (e2) => setSubjectName(e2.target.value),
105610
- placeholder: "e.g., Mathematics"
105611
- }
105612
- ))), /* @__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"))))));
105613
105622
  }
105614
105623
 
105615
105624
  // src/react-ui/components/metadata/GradeLevelManager.tsx
package/dist/react-ui.cjs CHANGED
@@ -168139,7 +168139,13 @@ init_react_shim();
168139
168139
 
168140
168140
  // src/react-ui/components/metadata/SubjectManager.tsx
168141
168141
  init_react_shim();
168142
- function SubjectManager() {
168142
+ function SubjectManager({
168143
+ initialData,
168144
+ isLoading: isLoadingProp,
168145
+ onAdd,
168146
+ onUpdate,
168147
+ onDelete
168148
+ }) {
168143
168149
  const [subjects, setSubjects] = React163.useState([]);
168144
168150
  const [isLoading, setIsLoading] = React163.useState(true);
168145
168151
  const [isDialogOpen, setIsDialogOpen] = React163.useState(false);
@@ -168150,20 +168156,27 @@ function SubjectManager() {
168150
168156
  const [itemToDelete, setItemToDelete] = React163.useState(null);
168151
168157
  const [isPending, startTransition] = React163.useTransition();
168152
168158
  const { toast: toast2 } = useToast();
168153
- React163.useEffect(() => {
168154
- setIsLoading(true);
168155
- try {
168156
- const data = MetadataService.getSubjects();
168157
- setSubjects(data);
168158
- } catch (error) {
168159
- toast2({ title: "Error", description: "Failed to fetch subjects from local storage.", variant: "destructive" });
168160
- } finally {
168161
- setIsLoading(false);
168162
- }
168163
- }, []);
168159
+ const isControlled = initialData !== void 0;
168164
168160
  const refreshData = () => {
168165
- 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
+ }
168166
168171
  };
168172
+ React163.useEffect(() => {
168173
+ if (isControlled) {
168174
+ setSubjects(initialData || []);
168175
+ setIsLoading(isLoadingProp || false);
168176
+ } else {
168177
+ refreshData();
168178
+ }
168179
+ }, [isControlled, initialData, isLoadingProp]);
168167
168180
  const handleAddItem = () => {
168168
168181
  setCurrentSubject(null);
168169
168182
  setSubjectName("");
@@ -168182,11 +168195,15 @@ function SubjectManager() {
168182
168195
  };
168183
168196
  const confirmDelete = () => {
168184
168197
  if (!itemToDelete) return;
168185
- startTransition(() => {
168198
+ startTransition(async () => {
168186
168199
  try {
168187
- MetadataService.deleteSubject(itemToDelete.code);
168200
+ if (isControlled && onDelete) {
168201
+ await onDelete(itemToDelete);
168202
+ } else {
168203
+ MetadataService.deleteSubject(itemToDelete.code);
168204
+ refreshData();
168205
+ }
168188
168206
  toast2({ title: "Success", description: `Subject "${itemToDelete.name}" deleted.` });
168189
- refreshData();
168190
168207
  } catch (error) {
168191
168208
  toast2({ title: "Error", description: error.message || "Failed to delete subject.", variant: "destructive" });
168192
168209
  } finally {
@@ -168200,40 +168217,32 @@ function SubjectManager() {
168200
168217
  toast2({ title: "Validation Error", description: "Please enter Subject Name and Subject Code.", variant: "destructive" });
168201
168218
  return;
168202
168219
  }
168203
- startTransition(() => {
168220
+ startTransition(async () => {
168204
168221
  try {
168205
168222
  if (currentSubject) {
168206
- 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
+ }
168207
168229
  toast2({ title: "Success", description: "Subject updated." });
168208
168230
  } else {
168209
- 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
+ }
168210
168237
  toast2({ title: "Success", description: "Subject added." });
168211
168238
  }
168212
- refreshData();
168213
168239
  setIsDialogOpen(false);
168214
168240
  } catch (error) {
168215
168241
  toast2({ title: "Error", description: error.message || "Failed to save subject.", variant: "destructive" });
168216
168242
  }
168217
168243
  });
168218
168244
  };
168219
- 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(
168220
- Input,
168221
- {
168222
- id: "subjectCode",
168223
- value: subjectCode,
168224
- onChange: (e3) => setSubjectCode(e3.target.value.toUpperCase()),
168225
- placeholder: "e.g., MATH",
168226
- disabled: !!currentSubject
168227
- }
168228
- )), /* @__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(
168229
- Input,
168230
- {
168231
- id: "subjectName",
168232
- value: subjectName,
168233
- onChange: (e3) => setSubjectName(e3.target.value),
168234
- placeholder: "e.g., Mathematics"
168235
- }
168236
- ))), /* @__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"))))));
168237
168246
  }
168238
168247
 
168239
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
@@ -168112,7 +168112,13 @@ init_react_shim();
168112
168112
 
168113
168113
  // src/react-ui/components/metadata/SubjectManager.tsx
168114
168114
  init_react_shim();
168115
- function SubjectManager() {
168115
+ function SubjectManager({
168116
+ initialData,
168117
+ isLoading: isLoadingProp,
168118
+ onAdd,
168119
+ onUpdate,
168120
+ onDelete
168121
+ }) {
168116
168122
  const [subjects, setSubjects] = useState([]);
168117
168123
  const [isLoading, setIsLoading] = useState(true);
168118
168124
  const [isDialogOpen, setIsDialogOpen] = useState(false);
@@ -168123,20 +168129,27 @@ function SubjectManager() {
168123
168129
  const [itemToDelete, setItemToDelete] = useState(null);
168124
168130
  const [isPending, startTransition] = useTransition();
168125
168131
  const { toast: toast2 } = useToast();
168126
- useEffect(() => {
168127
- setIsLoading(true);
168128
- try {
168129
- const data = MetadataService.getSubjects();
168130
- setSubjects(data);
168131
- } catch (error) {
168132
- toast2({ title: "Error", description: "Failed to fetch subjects from local storage.", variant: "destructive" });
168133
- } finally {
168134
- setIsLoading(false);
168135
- }
168136
- }, []);
168132
+ const isControlled = initialData !== void 0;
168137
168133
  const refreshData = () => {
168138
- 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
+ }
168139
168144
  };
168145
+ useEffect(() => {
168146
+ if (isControlled) {
168147
+ setSubjects(initialData || []);
168148
+ setIsLoading(isLoadingProp || false);
168149
+ } else {
168150
+ refreshData();
168151
+ }
168152
+ }, [isControlled, initialData, isLoadingProp]);
168140
168153
  const handleAddItem = () => {
168141
168154
  setCurrentSubject(null);
168142
168155
  setSubjectName("");
@@ -168155,11 +168168,15 @@ function SubjectManager() {
168155
168168
  };
168156
168169
  const confirmDelete = () => {
168157
168170
  if (!itemToDelete) return;
168158
- startTransition(() => {
168171
+ startTransition(async () => {
168159
168172
  try {
168160
- MetadataService.deleteSubject(itemToDelete.code);
168173
+ if (isControlled && onDelete) {
168174
+ await onDelete(itemToDelete);
168175
+ } else {
168176
+ MetadataService.deleteSubject(itemToDelete.code);
168177
+ refreshData();
168178
+ }
168161
168179
  toast2({ title: "Success", description: `Subject "${itemToDelete.name}" deleted.` });
168162
- refreshData();
168163
168180
  } catch (error) {
168164
168181
  toast2({ title: "Error", description: error.message || "Failed to delete subject.", variant: "destructive" });
168165
168182
  } finally {
@@ -168173,40 +168190,32 @@ function SubjectManager() {
168173
168190
  toast2({ title: "Validation Error", description: "Please enter Subject Name and Subject Code.", variant: "destructive" });
168174
168191
  return;
168175
168192
  }
168176
- startTransition(() => {
168193
+ startTransition(async () => {
168177
168194
  try {
168178
168195
  if (currentSubject) {
168179
- 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
+ }
168180
168202
  toast2({ title: "Success", description: "Subject updated." });
168181
168203
  } else {
168182
- 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
+ }
168183
168210
  toast2({ title: "Success", description: "Subject added." });
168184
168211
  }
168185
- refreshData();
168186
168212
  setIsDialogOpen(false);
168187
168213
  } catch (error) {
168188
168214
  toast2({ title: "Error", description: error.message || "Failed to save subject.", variant: "destructive" });
168189
168215
  }
168190
168216
  });
168191
168217
  };
168192
- 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(
168193
- Input,
168194
- {
168195
- id: "subjectCode",
168196
- value: subjectCode,
168197
- onChange: (e3) => setSubjectCode(e3.target.value.toUpperCase()),
168198
- placeholder: "e.g., MATH",
168199
- disabled: !!currentSubject
168200
- }
168201
- )), /* @__PURE__ */ React163__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React163__default.createElement(Label2, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React163__default.createElement(
168202
- Input,
168203
- {
168204
- id: "subjectName",
168205
- value: subjectName,
168206
- onChange: (e3) => setSubjectName(e3.target.value),
168207
- placeholder: "e.g., Mathematics"
168208
- }
168209
- ))), /* @__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"))))));
168210
168219
  }
168211
168220
 
168212
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.49",
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",