@thanh01.pmt/interactive-quiz-kit 1.0.23 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/authoring.js CHANGED
@@ -6,9 +6,9 @@ import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { GoogleGenAI } from '@google/genai';
8
8
  import * as React53 from 'react';
9
- import React53__default, { useRef, useState, useImperativeHandle, useCallback, useEffect, forwardRef } from 'react';
9
+ import React53__default, { useRef, useState, useImperativeHandle, useCallback, useEffect, forwardRef, useTransition, Suspense } from 'react';
10
10
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
11
- import { Circle, Check, ChevronDown, ChevronUp, X, Loader2, Play, CheckCircle, XCircle, RotateCcw, Save, Wand2, AlertTriangle, FileText, DownloadCloud, Settings, Upload, ListOrdered, ArrowUp, ArrowDown, Eye, Edit, Trash2, LogOut, PlusCircle, FileJson, Table, ArrowLeft, KeyRound } from 'lucide-react';
11
+ import { Circle, Check, ChevronDown, ChevronUp, X, Loader2, Play, CheckCircle, XCircle, RotateCcw, Save, KeyRound, Trash2, Wand2, AlertTriangle, FileText, DownloadCloud, Settings, Upload, ListOrdered, ArrowUp, ArrowDown, Eye, Edit, LogOut, Edit3, Search, BookCopy, PlusCircle, Award, Tag, Layers, Brain, HelpCircle, Lightbulb, ScanText, Settings2, FileJson, Table, ArrowLeft } from 'lucide-react';
12
12
  import * as LabelPrimitive from '@radix-ui/react-label';
13
13
  import { cva } from 'class-variance-authority';
14
14
  import ReactMarkdown from 'react-markdown';
@@ -28,6 +28,8 @@ import { useTranslation } from 'react-i18next';
28
28
  import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
29
29
  import * as DialogPrimitive from '@radix-ui/react-dialog';
30
30
  import * as SwitchPrimitives from '@radix-ui/react-switch';
31
+ import { format } from 'date-fns';
32
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
31
33
  import * as ToastPrimitives from '@radix-ui/react-toast';
32
34
 
33
35
  var __defProp = Object.defineProperty;
@@ -2659,6 +2661,260 @@ var KnowledgeCardService = class {
2659
2661
  }
2660
2662
  };
2661
2663
 
2664
+ // src/services/metadataService.ts
2665
+ var LocalStorageManager = class {
2666
+ constructor(key) {
2667
+ this.key = `iqk_metadata_${key}`;
2668
+ }
2669
+ getAll() {
2670
+ if (typeof window === "undefined") return [];
2671
+ try {
2672
+ const stored = localStorage.getItem(this.key);
2673
+ return stored ? JSON.parse(stored) : [];
2674
+ } catch (e) {
2675
+ console.error(`Error reading from localStorage key ${this.key}:`, e);
2676
+ return [];
2677
+ }
2678
+ }
2679
+ saveAll(items) {
2680
+ if (typeof window === "undefined") return;
2681
+ try {
2682
+ localStorage.setItem(this.key, JSON.stringify(items));
2683
+ } catch (e) {
2684
+ console.error(`Error writing to localStorage key ${this.key}:`, e);
2685
+ }
2686
+ }
2687
+ add(item) {
2688
+ const items = this.getAll();
2689
+ if (items.some((i) => i.code === item.code)) {
2690
+ throw new Error(`An item with code "${item.code}" already exists for ${this.key}.`);
2691
+ }
2692
+ const newItem = __spreadProps(__spreadValues({}, item), { id: generateUniqueId(`${this.key}_`) });
2693
+ this.saveAll([...items, newItem]);
2694
+ return newItem;
2695
+ }
2696
+ // ===== FIX IS HERE =====
2697
+ // Changed the type of 'updates' to allow 'code' to be part of the update object.
2698
+ update(id, updates) {
2699
+ const items = this.getAll();
2700
+ const index = items.findIndex((i) => i.id === id);
2701
+ if (index === -1) {
2702
+ console.warn(`Item with id "${id}" not found in ${this.key} for update.`);
2703
+ return null;
2704
+ }
2705
+ const updatedItem = __spreadValues(__spreadValues({}, items[index]), updates);
2706
+ items[index] = updatedItem;
2707
+ this.saveAll(items);
2708
+ return updatedItem;
2709
+ }
2710
+ // =======================
2711
+ delete(code) {
2712
+ const items = this.getAll();
2713
+ const newItems = items.filter((i) => i.code !== code);
2714
+ if (items.length === newItems.length) {
2715
+ return false;
2716
+ }
2717
+ this.saveAll(newItems);
2718
+ return true;
2719
+ }
2720
+ };
2721
+ var subjectManager = new LocalStorageManager("subjects");
2722
+ var gradeLevelManager = new LocalStorageManager("grade_levels");
2723
+ var topicManager = new LocalStorageManager("topics");
2724
+ var categoryManager = new LocalStorageManager("categories");
2725
+ var bloomLevelManager = new LocalStorageManager("bloom_levels");
2726
+ var questionTypeManager = new LocalStorageManager("question_types");
2727
+ var learningObjectiveManager = new LocalStorageManager("learning_objectives");
2728
+ var contextManager = new LocalStorageManager("contexts");
2729
+ var approachManager = new LocalStorageManager("approaches");
2730
+ function mapRawDifficultyToStandard(rawDifficulty) {
2731
+ switch (rawDifficulty) {
2732
+ case "E":
2733
+ case "E~M":
2734
+ return "easy";
2735
+ case "M":
2736
+ case "M~H":
2737
+ return "medium";
2738
+ case "H":
2739
+ return "hard";
2740
+ default:
2741
+ return "medium";
2742
+ }
2743
+ }
2744
+ var _MetadataService = class _MetadataService {
2745
+ };
2746
+ // --- Subject Services ---
2747
+ _MetadataService.getSubjects = () => subjectManager.getAll().sort((a, b) => a.name.localeCompare(b.name));
2748
+ _MetadataService.addSubject = (name, code) => {
2749
+ return subjectManager.add({ code, name, createdAt: (/* @__PURE__ */ new Date()).toISOString(), updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
2750
+ };
2751
+ _MetadataService.updateSubject = (id, name, code) => {
2752
+ return subjectManager.update(id, { name, code, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
2753
+ };
2754
+ _MetadataService.deleteSubject = (code) => {
2755
+ const topics = _MetadataService.getTopics(code);
2756
+ if (topics.length > 0) {
2757
+ throw new Error("Cannot delete subject: It is referenced by topics.");
2758
+ }
2759
+ return subjectManager.delete(code);
2760
+ };
2761
+ // --- GradeLevel Services ---
2762
+ _MetadataService.getGradeLevels = () => gradeLevelManager.getAll().sort((a, b) => a.name.localeCompare(b.name));
2763
+ _MetadataService.addGradeLevel = (name, code) => gradeLevelManager.add({ name, code });
2764
+ _MetadataService.updateGradeLevel = (id, name, code) => gradeLevelManager.update(id, { name, code });
2765
+ _MetadataService.deleteGradeLevel = (code) => gradeLevelManager.delete(code);
2766
+ // --- Topic Services ---
2767
+ _MetadataService.getTopics = (subjectCode) => {
2768
+ const allTopics = topicManager.getAll();
2769
+ const filtered = subjectCode ? allTopics.filter((t) => t.subjectCode === subjectCode) : allTopics;
2770
+ return filtered.sort((a, b) => a.name.localeCompare(b.name));
2771
+ };
2772
+ _MetadataService.addTopic = (name, code, subjectCode) => topicManager.add({ name, code, subjectCode });
2773
+ _MetadataService.updateTopic = (id, name, code, subjectCode) => topicManager.update(id, { name, code, subjectCode });
2774
+ _MetadataService.deleteTopic = (code) => topicManager.delete(code);
2775
+ // --- BloomLevel Services ---
2776
+ _MetadataService.getBloomLevels = () => bloomLevelManager.getAll().sort((a, b) => a.name.localeCompare(b.name));
2777
+ _MetadataService.addBloomLevel = (name, code, description) => bloomLevelManager.add({ name, code, description });
2778
+ _MetadataService.updateBloomLevel = (id, name, code, description) => bloomLevelManager.update(id, { name, code, description });
2779
+ _MetadataService.deleteBloomLevel = (code) => bloomLevelManager.delete(code);
2780
+ // --- QuestionType Services ---
2781
+ _MetadataService.getQuestionTypes = () => questionTypeManager.getAll().sort((a, b) => a.name.localeCompare(b.name));
2782
+ _MetadataService.addQuestionType = (name, code, description) => questionTypeManager.add({ name, code, description });
2783
+ _MetadataService.updateQuestionType = (id, name, code, description) => questionTypeManager.update(id, { name, code, description });
2784
+ _MetadataService.deleteQuestionType = (code) => questionTypeManager.delete(code);
2785
+ // --- Category Services ---
2786
+ _MetadataService.getCategories = () => categoryManager.getAll().sort((a, b) => a.name.localeCompare(b.name));
2787
+ _MetadataService.addCategory = (name, code, description) => categoryManager.add({ name, code, description });
2788
+ _MetadataService.updateCategory = (id, name, code, description) => categoryManager.update(id, { name, code, description });
2789
+ _MetadataService.deleteCategory = (code) => categoryManager.delete(code);
2790
+ // --- Context Services ---
2791
+ _MetadataService.getContexts = () => contextManager.getAll().sort((a, b) => a.name.localeCompare(b.name));
2792
+ _MetadataService.addContext = (name, code, description) => contextManager.add({ name, code, description });
2793
+ _MetadataService.updateContext = (id, name, code, description) => contextManager.update(id, { name, code, description });
2794
+ _MetadataService.deleteContext = (code) => contextManager.delete(code);
2795
+ // --- Approach Services ---
2796
+ _MetadataService.getApproaches = () => approachManager.getAll().sort((a, b) => a.code.localeCompare(b.code));
2797
+ _MetadataService.addApproach = (approachData) => {
2798
+ const difficulty = mapRawDifficultyToStandard(approachData.rawDifficulty);
2799
+ return approachManager.add(__spreadProps(__spreadValues({}, approachData), { difficulty }));
2800
+ };
2801
+ _MetadataService.updateApproach = (id, approachData) => {
2802
+ const updates = __spreadValues({}, approachData);
2803
+ if (approachData.rawDifficulty) {
2804
+ updates.difficulty = mapRawDifficultyToStandard(approachData.rawDifficulty);
2805
+ }
2806
+ return approachManager.update(id, updates);
2807
+ };
2808
+ _MetadataService.deleteApproach = (code) => approachManager.delete(code);
2809
+ // --- LearningObjective Services ---
2810
+ _MetadataService.getLearningObjectives = (subjectCode) => {
2811
+ const allLOs = learningObjectiveManager.getAll();
2812
+ const filtered = subjectCode ? allLOs.filter((lo) => lo.subjectCode === subjectCode) : allLOs;
2813
+ return filtered.sort((a, b) => a.name.localeCompare(b.name));
2814
+ };
2815
+ _MetadataService.addLearningObjective = (name, code, subjectCode, description) => learningObjectiveManager.add({ name, code, subjectCode, description });
2816
+ _MetadataService.updateLearningObjective = (id, name, code, subjectCode, description) => learningObjectiveManager.update(id, { name, code, subjectCode, description });
2817
+ _MetadataService.deleteLearningObjective = (code) => learningObjectiveManager.delete(code);
2818
+ var MetadataService = _MetadataService;
2819
+
2820
+ // src/services/questionBankService.ts
2821
+ var LocalStorageManager2 = class {
2822
+ constructor(key) {
2823
+ this.key = key;
2824
+ }
2825
+ getAll() {
2826
+ if (typeof window === "undefined") return [];
2827
+ try {
2828
+ const stored = localStorage.getItem(this.key);
2829
+ return stored ? JSON.parse(stored) : [];
2830
+ } catch (e) {
2831
+ console.error(`Error reading from localStorage key ${this.key}:`, e);
2832
+ return [];
2833
+ }
2834
+ }
2835
+ saveAll(items) {
2836
+ if (typeof window === "undefined") return;
2837
+ try {
2838
+ localStorage.setItem(this.key, JSON.stringify(items));
2839
+ } catch (e) {
2840
+ console.error(`Error writing to localStorage key ${this.key}:`, e);
2841
+ }
2842
+ }
2843
+ };
2844
+ var questionBankManager = new LocalStorageManager2("iqk_question_bank");
2845
+ var QuestionBankService = class {
2846
+ static getQuestions(filters) {
2847
+ let questions = questionBankManager.getAll();
2848
+ if (filters) {
2849
+ if (filters.subjectCode) {
2850
+ questions = questions.filter((q) => q.subjectCode === filters.subjectCode);
2851
+ }
2852
+ if (filters.topicCode) {
2853
+ questions = questions.filter((q) => q.topicCode === filters.topicCode);
2854
+ }
2855
+ if (filters.gradeLevelCode) {
2856
+ questions = questions.filter((q) => q.gradeLevelCode === filters.gradeLevelCode);
2857
+ }
2858
+ if (filters.bloomLevelCode) {
2859
+ questions = questions.filter((q) => q.bloomLevelCode === filters.bloomLevelCode);
2860
+ }
2861
+ if (filters.questionTypeCode) {
2862
+ questions = questions.filter((q) => q.questionTypeCode === filters.questionTypeCode);
2863
+ }
2864
+ if (filters.difficulty) {
2865
+ questions = questions.filter((q) => q.difficulty === filters.difficulty);
2866
+ }
2867
+ if (filters.searchTerm) {
2868
+ const lowercasedTerm = filters.searchTerm.toLowerCase();
2869
+ questions = questions.filter(
2870
+ (q) => q.text.toLowerCase().includes(lowercasedTerm) || q.code.toLowerCase().includes(lowercasedTerm)
2871
+ );
2872
+ }
2873
+ }
2874
+ return questions.sort((a, b) => new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime());
2875
+ }
2876
+ static getQuestionByCode(code) {
2877
+ return questionBankManager.getAll().find((q) => q.code === code);
2878
+ }
2879
+ // CHANGE 2: Simplified function signature. It now takes the full object to be added.
2880
+ static addQuestion(questionData) {
2881
+ const allQuestions = questionBankManager.getAll();
2882
+ if (allQuestions.some((q) => q.code === questionData.code)) {
2883
+ throw new Error(`A question with code "${questionData.code}" already exists.`);
2884
+ }
2885
+ const newQuestion = __spreadProps(__spreadValues({}, questionData), {
2886
+ id: generateUniqueId("qb_"),
2887
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
2888
+ });
2889
+ questionBankManager.saveAll([...allQuestions, newQuestion]);
2890
+ return newQuestion;
2891
+ }
2892
+ // CHANGE 2: Simplified function signature.
2893
+ static updateQuestion(id, updates) {
2894
+ const allQuestions = questionBankManager.getAll();
2895
+ const index = allQuestions.findIndex((q) => q.id === id);
2896
+ if (index === -1) {
2897
+ console.warn(`Question with id "${id}" not found for update.`);
2898
+ return null;
2899
+ }
2900
+ const updatedQuestion = __spreadProps(__spreadValues(__spreadValues({}, allQuestions[index]), updates), {
2901
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
2902
+ });
2903
+ allQuestions[index] = updatedQuestion;
2904
+ questionBankManager.saveAll(allQuestions);
2905
+ return updatedQuestion;
2906
+ }
2907
+ static deleteQuestionByCode(code) {
2908
+ const allQuestions = questionBankManager.getAll();
2909
+ const newQuestions = allQuestions.filter((q) => q.code !== code);
2910
+ if (allQuestions.length === newQuestions.length) {
2911
+ return false;
2912
+ }
2913
+ questionBankManager.saveAll(newQuestions);
2914
+ return true;
2915
+ }
2916
+ };
2917
+
2662
2918
  // src/services/HTMLLauncherGenerator.ts
2663
2919
  var escapeAttribute = (unsafe) => {
2664
2920
  if (typeof unsafe !== "string") return "";
@@ -11091,6 +11347,1498 @@ var QuizAuthoringTool = ({
11091
11347
  }
11092
11348
  ));
11093
11349
  };
11350
+ var Table2 = React53.forwardRef((_a, ref) => {
11351
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11352
+ return /* @__PURE__ */ React53.createElement("div", { className: "relative w-full overflow-auto" }, /* @__PURE__ */ React53.createElement(
11353
+ "table",
11354
+ __spreadValues({
11355
+ ref,
11356
+ className: cn("w-full caption-bottom text-sm", className)
11357
+ }, props)
11358
+ ));
11359
+ });
11360
+ Table2.displayName = "Table";
11361
+ var TableHeader = React53.forwardRef((_a, ref) => {
11362
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11363
+ return /* @__PURE__ */ React53.createElement("thead", __spreadValues({ ref, className: cn("[&_tr]:border-b", className) }, props));
11364
+ });
11365
+ TableHeader.displayName = "TableHeader";
11366
+ var TableBody = React53.forwardRef((_a, ref) => {
11367
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11368
+ return /* @__PURE__ */ React53.createElement(
11369
+ "tbody",
11370
+ __spreadValues({
11371
+ ref,
11372
+ className: cn("[&_tr:last-child]:border-0", className)
11373
+ }, props)
11374
+ );
11375
+ });
11376
+ TableBody.displayName = "TableBody";
11377
+ var TableFooter = React53.forwardRef((_a, ref) => {
11378
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11379
+ return /* @__PURE__ */ React53.createElement(
11380
+ "tfoot",
11381
+ __spreadValues({
11382
+ ref,
11383
+ className: cn(
11384
+ "border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
11385
+ className
11386
+ )
11387
+ }, props)
11388
+ );
11389
+ });
11390
+ TableFooter.displayName = "TableFooter";
11391
+ var TableRow = React53.forwardRef((_a, ref) => {
11392
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11393
+ return /* @__PURE__ */ React53.createElement(
11394
+ "tr",
11395
+ __spreadValues({
11396
+ ref,
11397
+ className: cn(
11398
+ "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
11399
+ className
11400
+ )
11401
+ }, props)
11402
+ );
11403
+ });
11404
+ TableRow.displayName = "TableRow";
11405
+ var TableHead = React53.forwardRef((_a, ref) => {
11406
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11407
+ return /* @__PURE__ */ React53.createElement(
11408
+ "th",
11409
+ __spreadValues({
11410
+ ref,
11411
+ className: cn(
11412
+ "h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
11413
+ className
11414
+ )
11415
+ }, props)
11416
+ );
11417
+ });
11418
+ TableHead.displayName = "TableHead";
11419
+ var TableCell = React53.forwardRef((_a, ref) => {
11420
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11421
+ return /* @__PURE__ */ React53.createElement(
11422
+ "td",
11423
+ __spreadValues({
11424
+ ref,
11425
+ className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)
11426
+ }, props)
11427
+ );
11428
+ });
11429
+ TableCell.displayName = "TableCell";
11430
+ var TableCaption = React53.forwardRef((_a, ref) => {
11431
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11432
+ return /* @__PURE__ */ React53.createElement(
11433
+ "caption",
11434
+ __spreadValues({
11435
+ ref,
11436
+ className: cn("mt-4 text-sm text-muted-foreground", className)
11437
+ }, props)
11438
+ );
11439
+ });
11440
+ TableCaption.displayName = "TableCaption";
11441
+ function QuestionList({
11442
+ questions,
11443
+ onEdit,
11444
+ onDelete,
11445
+ onView
11446
+ }) {
11447
+ const [metadata, setMetadata] = useState({ subjects: [], topics: [], gradeLevels: [], questionTypes: [], bloomLevels: [] });
11448
+ useEffect(() => {
11449
+ const subjectsData = MetadataService.getSubjects();
11450
+ const topicsData = MetadataService.getTopics();
11451
+ const gradeLevelsData = MetadataService.getGradeLevels();
11452
+ const questionTypesData = MetadataService.getQuestionTypes();
11453
+ const bloomLevelsData = MetadataService.getBloomLevels();
11454
+ setMetadata({
11455
+ subjects: subjectsData,
11456
+ topics: topicsData,
11457
+ gradeLevels: gradeLevelsData,
11458
+ questionTypes: questionTypesData,
11459
+ bloomLevels: bloomLevelsData
11460
+ });
11461
+ }, []);
11462
+ const getLookupName = (code, items) => {
11463
+ var _a;
11464
+ if (!code || !items) return "N/A";
11465
+ return ((_a = items.find((item) => item.code === code)) == null ? void 0 : _a.name) || code;
11466
+ };
11467
+ if (questions.length === 0) {
11468
+ return /* @__PURE__ */ React53__default.createElement("p", { className: "text-center text-muted-foreground py-8" }, "No questions match the current filters. Try adjusting your search or add new questions.");
11469
+ }
11470
+ return /* @__PURE__ */ React53__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React53__default.createElement(Table2, null, /* @__PURE__ */ React53__default.createElement(TableHeader, null, /* @__PURE__ */ React53__default.createElement(TableRow, null, /* @__PURE__ */ React53__default.createElement(TableHead, { className: "w-[30%]" }, "Question Text"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Type"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Topic"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Grade"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Bloom's"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Last Modified"), /* @__PURE__ */ React53__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React53__default.createElement(TableBody, null, questions.map((question) => /* @__PURE__ */ React53__default.createElement(TableRow, { key: question.id }, /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-medium max-w-xs truncate", title: question.text }, question.text), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-mono text-xs" }, question.code), /* @__PURE__ */ React53__default.createElement(TableCell, null, /* @__PURE__ */ React53__default.createElement(Badge, { variant: "secondary" }, getLookupName(question.questionTypeCode, metadata.questionTypes))), /* @__PURE__ */ React53__default.createElement(TableCell, null, getLookupName(question.subjectCode, metadata.subjects)), /* @__PURE__ */ React53__default.createElement(TableCell, null, getLookupName(question.topicCode, metadata.topics)), /* @__PURE__ */ React53__default.createElement(TableCell, null, getLookupName(question.gradeLevelCode, metadata.gradeLevels)), /* @__PURE__ */ React53__default.createElement(TableCell, null, /* @__PURE__ */ React53__default.createElement(Badge, { variant: "outline" }, getLookupName(question.bloomLevelCode, metadata.bloomLevels))), /* @__PURE__ */ React53__default.createElement(TableCell, null, format(new Date(question.lastModified), "MMM d, yyyy")), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "text-right" }, onView && /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onView(question), className: "mr-1" }, /* @__PURE__ */ React53__default.createElement(Eye, { className: "h-4 w-4" })), /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onEdit(question), className: "mr-1" }, /* @__PURE__ */ React53__default.createElement(Edit3, { className: "h-4 w-4" })), /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => onDelete(question), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React53__default.createElement(Trash2, { className: "h-4 w-4" }))))))));
11471
+ }
11472
+ var ALL_ITEMS_VALUE = "_ALL_ITEMS_";
11473
+ function QuestionFilters({
11474
+ onFilterChange,
11475
+ initialFilters = {}
11476
+ }) {
11477
+ const [searchTerm, setSearchTerm] = useState(initialFilters.searchTerm || "");
11478
+ const [subjectCode, setSubjectCode] = useState(initialFilters.subjectCode || "");
11479
+ const [topicCode, setTopicCode] = useState(initialFilters.topicCode || "");
11480
+ const [gradeLevelCode, setGradeLevelCode] = useState(initialFilters.gradeLevelCode || "");
11481
+ const [bloomLevelCode, setBloomLevelCode] = useState(initialFilters.bloomLevelCode || "");
11482
+ const [questionTypeCode, setQuestionTypeCode] = useState(initialFilters.questionTypeCode || "");
11483
+ const [difficulty, setDifficulty] = useState(initialFilters.difficulty || "");
11484
+ const [subjects, setSubjects] = useState([]);
11485
+ const [allTopics, setAllTopics] = useState([]);
11486
+ const [gradeLevels, setGradeLevels] = useState([]);
11487
+ const [questionTypes, setQuestionTypes] = useState([]);
11488
+ const [bloomLevels, setBloomLevels] = useState([]);
11489
+ const [filteredTopics, setFilteredTopics] = useState([]);
11490
+ useEffect(() => {
11491
+ const subjectsData = MetadataService.getSubjects();
11492
+ const topicsData = MetadataService.getTopics();
11493
+ const gradeLevelsData = MetadataService.getGradeLevels();
11494
+ const questionTypesData = MetadataService.getQuestionTypes();
11495
+ const bloomLevelsData = MetadataService.getBloomLevels();
11496
+ setSubjects(subjectsData);
11497
+ setAllTopics(topicsData);
11498
+ setGradeLevels(gradeLevelsData);
11499
+ setQuestionTypes(questionTypesData);
11500
+ setBloomLevels(bloomLevelsData);
11501
+ }, []);
11502
+ useEffect(() => {
11503
+ if (subjectCode) {
11504
+ const relatedTopics = allTopics.filter((t) => t.subjectCode === subjectCode);
11505
+ setFilteredTopics(relatedTopics);
11506
+ if (topicCode && !relatedTopics.find((t) => t.code === topicCode)) {
11507
+ setTopicCode("");
11508
+ }
11509
+ } else {
11510
+ setFilteredTopics(allTopics);
11511
+ setTopicCode("");
11512
+ }
11513
+ }, [subjectCode, allTopics, topicCode]);
11514
+ const handleApplyFilters = () => {
11515
+ onFilterChange({
11516
+ searchTerm: searchTerm || void 0,
11517
+ subjectCode: subjectCode || void 0,
11518
+ topicCode: topicCode || void 0,
11519
+ gradeLevelCode: gradeLevelCode || void 0,
11520
+ bloomLevelCode: bloomLevelCode || void 0,
11521
+ questionTypeCode: questionTypeCode || void 0,
11522
+ difficulty: difficulty || void 0
11523
+ });
11524
+ };
11525
+ const handleClearFilters = () => {
11526
+ setSearchTerm("");
11527
+ setSubjectCode("");
11528
+ setTopicCode("");
11529
+ setGradeLevelCode("");
11530
+ setBloomLevelCode("");
11531
+ setQuestionTypeCode("");
11532
+ setDifficulty("");
11533
+ onFilterChange({});
11534
+ };
11535
+ const createSelectHandler = (setter) => {
11536
+ return (selectedValue) => {
11537
+ setter(selectedValue === ALL_ITEMS_VALUE ? "" : selectedValue);
11538
+ };
11539
+ };
11540
+ return /* @__PURE__ */ React53__default.createElement("div", { className: "p-4 mb-6 bg-card border rounded-lg shadow-sm" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-7 gap-4 items-end" }, /* @__PURE__ */ React53__default.createElement(
11541
+ Input,
11542
+ {
11543
+ placeholder: "Search questions...",
11544
+ value: searchTerm,
11545
+ onChange: (e) => setSearchTerm(e.target.value),
11546
+ className: "lg:col-span-2 xl:col-span-1"
11547
+ }
11548
+ ), /* @__PURE__ */ React53__default.createElement(Select, { value: subjectCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setSubjectCode) }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, null, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Subject" })), /* @__PURE__ */ React53__default.createElement(SelectContent, null, /* @__PURE__ */ React53__default.createElement(SelectItem, { value: ALL_ITEMS_VALUE }, "All Subjects"), subjects.map((s) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: s.code, value: s.code }, s.name)))), /* @__PURE__ */ React53__default.createElement(Select, { value: topicCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setTopicCode), disabled: !subjectCode || filteredTopics.length === 0 }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, null, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Topic" })), /* @__PURE__ */ React53__default.createElement(SelectContent, null, /* @__PURE__ */ React53__default.createElement(SelectItem, { value: ALL_ITEMS_VALUE }, "All Topics"), filteredTopics.map((t) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: t.code, value: t.code }, t.name)))), /* @__PURE__ */ React53__default.createElement(Select, { value: gradeLevelCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setGradeLevelCode) }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, null, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Grade Level" })), /* @__PURE__ */ React53__default.createElement(SelectContent, null, /* @__PURE__ */ React53__default.createElement(SelectItem, { value: ALL_ITEMS_VALUE }, "All Grade Levels"), gradeLevels.map((gl) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: gl.code, value: gl.code }, gl.name)))), /* @__PURE__ */ React53__default.createElement(Select, { value: bloomLevelCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setBloomLevelCode) }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, null, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Bloom's Level" })), /* @__PURE__ */ React53__default.createElement(SelectContent, null, /* @__PURE__ */ React53__default.createElement(SelectItem, { value: ALL_ITEMS_VALUE }, "All Levels"), bloomLevels.map((bl) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: bl.code, value: bl.code }, bl.name)))), /* @__PURE__ */ React53__default.createElement(Select, { value: questionTypeCode || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setQuestionTypeCode) }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, null, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Question Type" })), /* @__PURE__ */ React53__default.createElement(SelectContent, null, /* @__PURE__ */ React53__default.createElement(SelectItem, { value: ALL_ITEMS_VALUE }, "All Types"), questionTypes.map((qt) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: qt.code, value: qt.code }, qt.name)))), /* @__PURE__ */ React53__default.createElement(Select, { value: difficulty || ALL_ITEMS_VALUE, onValueChange: createSelectHandler(setDifficulty) }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, null, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Difficulty" })), /* @__PURE__ */ React53__default.createElement(SelectContent, null, /* @__PURE__ */ React53__default.createElement(SelectItem, { value: ALL_ITEMS_VALUE }, "All Difficulties"), /* @__PURE__ */ React53__default.createElement(SelectItem, { value: "easy" }, "Easy"), /* @__PURE__ */ React53__default.createElement(SelectItem, { value: "medium" }, "Medium"), /* @__PURE__ */ React53__default.createElement(SelectItem, { value: "hard" }, "Hard"))), /* @__PURE__ */ React53__default.createElement("div", { className: "flex gap-2 col-span-full sm:col-span-1 xl:col-span-2 xl:col-start-6" }, /* @__PURE__ */ React53__default.createElement(Button, { onClick: handleApplyFilters, className: "w-full sm:w-auto flex-grow" }, /* @__PURE__ */ React53__default.createElement(Search, { className: "mr-2 h-4 w-4" }), " Apply"), /* @__PURE__ */ React53__default.createElement(Button, { onClick: handleClearFilters, variant: "outline", className: "w-full sm:w-auto flex-grow" }, /* @__PURE__ */ React53__default.createElement(XCircle, { className: "mr-2 h-4 w-4" }), " Clear"))));
11549
+ }
11550
+ function QuestionFormDialog({
11551
+ isOpen,
11552
+ onOpenChange,
11553
+ onSave,
11554
+ questionToEdit
11555
+ }) {
11556
+ const [code, setCode] = useState("");
11557
+ const [subjectCode, setSubjectCode] = useState("");
11558
+ const [topicCode, setTopicCode] = useState("");
11559
+ const [gradeLevelCode, setGradeLevelCode] = useState("");
11560
+ const [bloomLevelCode, setBloomLevelCode] = useState("");
11561
+ const [isQuestionEditorOpen, setIsQuestionEditorOpen] = useState(false);
11562
+ const [questionConfig, setQuestionConfig] = useState(null);
11563
+ const [subjects, setSubjects] = useState([]);
11564
+ const [topics, setTopics] = useState([]);
11565
+ const [gradeLevels, setGradeLevels] = useState([]);
11566
+ const [bloomLevels, setBloomLevels] = useState([]);
11567
+ const [filteredTopics, setFilteredTopics] = useState([]);
11568
+ const [isPending, startTransition] = useTransition();
11569
+ const { toast: toast2 } = useToast();
11570
+ useEffect(() => {
11571
+ var _a, _b, _c;
11572
+ const subjectsData = MetadataService.getSubjects();
11573
+ const topicsData = MetadataService.getTopics();
11574
+ const gradeLevelsData = MetadataService.getGradeLevels();
11575
+ const bloomLevelsData = MetadataService.getBloomLevels();
11576
+ setSubjects(subjectsData);
11577
+ setTopics(topicsData);
11578
+ setGradeLevels(gradeLevelsData);
11579
+ setBloomLevels(bloomLevelsData);
11580
+ if (isOpen) {
11581
+ if (questionToEdit) {
11582
+ setCode(questionToEdit.code);
11583
+ setSubjectCode(questionToEdit.subjectCode);
11584
+ setTopicCode(questionToEdit.topicCode);
11585
+ setGradeLevelCode(questionToEdit.gradeLevelCode);
11586
+ setBloomLevelCode(questionToEdit.bloomLevelCode);
11587
+ setQuestionConfig(questionToEdit.questionConfig);
11588
+ } else {
11589
+ const defaultSubject = ((_a = subjectsData[0]) == null ? void 0 : _a.code) || "";
11590
+ const defaultGrade = ((_b = gradeLevelsData[0]) == null ? void 0 : _b.code) || "";
11591
+ const defaultBloom = ((_c = bloomLevelsData[0]) == null ? void 0 : _c.code) || "";
11592
+ setCode("");
11593
+ setSubjectCode(defaultSubject);
11594
+ setGradeLevelCode(defaultGrade);
11595
+ setBloomLevelCode(defaultBloom);
11596
+ setTopicCode("");
11597
+ setQuestionConfig(null);
11598
+ }
11599
+ }
11600
+ }, [questionToEdit, isOpen]);
11601
+ useEffect(() => {
11602
+ var _a;
11603
+ if (subjectCode) {
11604
+ const relatedTopics = topics.filter((t) => t.subjectCode === subjectCode);
11605
+ setFilteredTopics(relatedTopics);
11606
+ if (topicCode && !relatedTopics.find((t) => t.code === topicCode)) {
11607
+ setTopicCode(((_a = relatedTopics[0]) == null ? void 0 : _a.code) || "");
11608
+ }
11609
+ } else {
11610
+ setFilteredTopics([]);
11611
+ setTopicCode("");
11612
+ }
11613
+ }, [subjectCode, topics, topicCode]);
11614
+ const handleOpenQuestionEditor = (type) => {
11615
+ if (questionToEdit && questionConfig) {
11616
+ setIsQuestionEditorOpen(true);
11617
+ } else if (type) {
11618
+ const newTemplate = QuizEditorService.createNewQuestionTemplate(type);
11619
+ setQuestionConfig(newTemplate);
11620
+ setIsQuestionEditorOpen(true);
11621
+ }
11622
+ };
11623
+ const handleQuestionConfigSave = (savedConfig) => {
11624
+ setQuestionConfig(savedConfig);
11625
+ setIsQuestionEditorOpen(false);
11626
+ };
11627
+ const handleSubmit = () => {
11628
+ if (!code.trim() || !subjectCode || !topicCode || !gradeLevelCode || !bloomLevelCode) {
11629
+ toast2({ title: "Validation Error", description: "Please fill all metadata fields (Code, Subject, Topic, etc.).", variant: "destructive" });
11630
+ return;
11631
+ }
11632
+ if (!questionConfig) {
11633
+ toast2({ title: "Validation Error", description: "Question content is missing. Please create or edit the question details.", variant: "destructive" });
11634
+ return;
11635
+ }
11636
+ startTransition(() => {
11637
+ try {
11638
+ const finalDataPayload = {
11639
+ code,
11640
+ text: questionConfig.prompt.replace(/<[^>]*>?/gm, "").substring(0, 200),
11641
+ subjectCode,
11642
+ topicCode,
11643
+ gradeLevelCode,
11644
+ bloomLevelCode,
11645
+ questionTypeCode: questionConfig.questionType,
11646
+ difficulty: questionConfig.difficulty,
11647
+ questionConfig: __spreadProps(__spreadValues({}, questionConfig), {
11648
+ subject: subjectCode,
11649
+ topic: topicCode,
11650
+ gradeBand: gradeLevelCode,
11651
+ bloomLevel: bloomLevelCode
11652
+ })
11653
+ };
11654
+ if (questionToEdit) {
11655
+ QuestionBankService.updateQuestion(questionToEdit.id, finalDataPayload);
11656
+ toast2({ title: "Success", description: "Question updated." });
11657
+ } else {
11658
+ QuestionBankService.addQuestion(finalDataPayload);
11659
+ toast2({ title: "Success", description: "Question added." });
11660
+ }
11661
+ onSave();
11662
+ onOpenChange(false);
11663
+ } catch (error) {
11664
+ toast2({ title: "Error", description: error.message || "Failed to save question.", variant: "destructive" });
11665
+ }
11666
+ });
11667
+ };
11668
+ const dialogTitle = questionToEdit ? "Edit Question Metadata" : "Create New Question";
11669
+ const dialogDescription = "First, define the metadata for the question. Then, edit the question's content and logic.";
11670
+ return /* @__PURE__ */ React53__default.createElement(React53__default.Fragment, null, /* @__PURE__ */ React53__default.createElement(Dialog, { open: isOpen, onOpenChange }, /* @__PURE__ */ React53__default.createElement(DialogContent, { className: "sm:max-w-xl md:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React53__default.createElement(DialogHeader, null, /* @__PURE__ */ React53__default.createElement(DialogTitle, null, dialogTitle), /* @__PURE__ */ React53__default.createElement(DialogDescription, null, dialogDescription)), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "code" }, "Question Code"), /* @__PURE__ */ React53__default.createElement(Input, { id: "code", value: code, onChange: (e) => setCode(e.target.value.toUpperCase()), placeholder: "Unique code (e.g., MATH-ALG-001)", disabled: !!questionToEdit })), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "gradeLevelCode" }, "Grade Level"), /* @__PURE__ */ React53__default.createElement(Select, { value: gradeLevelCode, onValueChange: setGradeLevelCode }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, { id: "gradeLevelCode" }, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Select Grade Level" })), /* @__PURE__ */ React53__default.createElement(SelectContent, null, gradeLevels.map((gl) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: gl.code, value: gl.code }, gl.name)))))), /* @__PURE__ */ React53__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "subjectCode" }, "Subject"), /* @__PURE__ */ React53__default.createElement(Select, { value: subjectCode, onValueChange: setSubjectCode }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, { id: "subjectCode" }, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Select Subject" })), /* @__PURE__ */ React53__default.createElement(SelectContent, null, subjects.map((s) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: s.code, value: s.code }, s.name))))), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "topicCode" }, "Topic"), /* @__PURE__ */ React53__default.createElement(Select, { value: topicCode, onValueChange: setTopicCode, disabled: !subjectCode || filteredTopics.length === 0 }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, { id: "topicCode" }, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Select Topic" })), /* @__PURE__ */ React53__default.createElement(SelectContent, null, filteredTopics.map((t) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: t.code, value: t.code }, t.name)))))), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "bloomLevelCode" }, "Bloom's Level"), /* @__PURE__ */ React53__default.createElement(Select, { value: bloomLevelCode, onValueChange: setBloomLevelCode }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, { id: "bloomLevelCode" }, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Select Bloom's Level" })), /* @__PURE__ */ React53__default.createElement(SelectContent, null, bloomLevels.map((bl) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: bl.code, value: bl.code }, bl.name))))), /* @__PURE__ */ React53__default.createElement("div", { className: "pt-4 border-t" }, /* @__PURE__ */ React53__default.createElement(Label, { className: "font-semibold" }, "Question Content & Logic"), questionConfig ? /* @__PURE__ */ React53__default.createElement("div", { className: "p-3 mt-2 border rounded-md bg-muted/30 flex justify-between items-center" }, /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement("p", { className: "font-medium" }, "Type: ", /* @__PURE__ */ React53__default.createElement("span", { className: "font-normal" }, questionConfig.questionType)), /* @__PURE__ */ React53__default.createElement("p", { className: "text-sm text-muted-foreground truncate max-w-md" }, "Prompt: ", questionConfig.prompt.replace(/<[^>]*>?/gm, "") || "Not set")), /* @__PURE__ */ React53__default.createElement(Button, { variant: "outline", onClick: () => handleOpenQuestionEditor() }, /* @__PURE__ */ React53__default.createElement(Edit, { className: "mr-2 h-4 w-4" }), " Edit Content")) : /* @__PURE__ */ React53__default.createElement("div", { className: "p-3 mt-2 border-dashed border-2 rounded-md text-center" }, /* @__PURE__ */ React53__default.createElement("p", { className: "text-muted-foreground mb-2" }, "No content has been created yet."), /* @__PURE__ */ React53__default.createElement(Button, { variant: "default", onClick: () => handleOpenQuestionEditor("multiple_choice") }, /* @__PURE__ */ React53__default.createElement(BookCopy, { className: "mr-2 h-4 w-4" }), " Create Question Content")))), /* @__PURE__ */ React53__default.createElement(DialogFooter, null, /* @__PURE__ */ React53__default.createElement(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !questionConfig }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), " Save Question")))), questionConfig && /* @__PURE__ */ React53__default.createElement(
11671
+ EditQuestionModal,
11672
+ {
11673
+ isOpen: isQuestionEditorOpen,
11674
+ onClose: () => setIsQuestionEditorOpen(false),
11675
+ questionData: questionConfig,
11676
+ onSave: handleQuestionConfigSave
11677
+ }
11678
+ ));
11679
+ }
11680
+ var AlertDialog = AlertDialogPrimitive.Root;
11681
+ var AlertDialogPortal = AlertDialogPrimitive.Portal;
11682
+ var AlertDialogOverlay = React53.forwardRef((_a, ref) => {
11683
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11684
+ return /* @__PURE__ */ React53.createElement(
11685
+ AlertDialogPrimitive.Overlay,
11686
+ __spreadProps(__spreadValues({
11687
+ className: cn(
11688
+ "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
11689
+ className
11690
+ )
11691
+ }, props), {
11692
+ ref
11693
+ })
11694
+ );
11695
+ });
11696
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
11697
+ var AlertDialogContent = React53.forwardRef((_a, ref) => {
11698
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11699
+ return /* @__PURE__ */ React53.createElement(AlertDialogPortal, null, /* @__PURE__ */ React53.createElement(AlertDialogOverlay, null), /* @__PURE__ */ React53.createElement(
11700
+ AlertDialogPrimitive.Content,
11701
+ __spreadValues({
11702
+ ref,
11703
+ className: cn(
11704
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
11705
+ className
11706
+ )
11707
+ }, props)
11708
+ ));
11709
+ });
11710
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
11711
+ var AlertDialogHeader = (_a) => {
11712
+ var _b = _a, {
11713
+ className
11714
+ } = _b, props = __objRest(_b, [
11715
+ "className"
11716
+ ]);
11717
+ return /* @__PURE__ */ React53.createElement(
11718
+ "div",
11719
+ __spreadValues({
11720
+ className: cn(
11721
+ "flex flex-col space-y-2 text-center sm:text-left",
11722
+ className
11723
+ )
11724
+ }, props)
11725
+ );
11726
+ };
11727
+ AlertDialogHeader.displayName = "AlertDialogHeader";
11728
+ var AlertDialogFooter = (_a) => {
11729
+ var _b = _a, {
11730
+ className
11731
+ } = _b, props = __objRest(_b, [
11732
+ "className"
11733
+ ]);
11734
+ return /* @__PURE__ */ React53.createElement(
11735
+ "div",
11736
+ __spreadValues({
11737
+ className: cn(
11738
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
11739
+ className
11740
+ )
11741
+ }, props)
11742
+ );
11743
+ };
11744
+ AlertDialogFooter.displayName = "AlertDialogFooter";
11745
+ var AlertDialogTitle = React53.forwardRef((_a, ref) => {
11746
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11747
+ return /* @__PURE__ */ React53.createElement(
11748
+ AlertDialogPrimitive.Title,
11749
+ __spreadValues({
11750
+ ref,
11751
+ className: cn("text-lg font-semibold", className)
11752
+ }, props)
11753
+ );
11754
+ });
11755
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
11756
+ var AlertDialogDescription = React53.forwardRef((_a, ref) => {
11757
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11758
+ return /* @__PURE__ */ React53.createElement(
11759
+ AlertDialogPrimitive.Description,
11760
+ __spreadValues({
11761
+ ref,
11762
+ className: cn("text-sm text-muted-foreground", className)
11763
+ }, props)
11764
+ );
11765
+ });
11766
+ AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
11767
+ var AlertDialogAction = React53.forwardRef((_a, ref) => {
11768
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11769
+ return /* @__PURE__ */ React53.createElement(
11770
+ AlertDialogPrimitive.Action,
11771
+ __spreadValues({
11772
+ ref,
11773
+ className: cn(buttonVariants(), className)
11774
+ }, props)
11775
+ );
11776
+ });
11777
+ AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
11778
+ var AlertDialogCancel = React53.forwardRef((_a, ref) => {
11779
+ var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
11780
+ return /* @__PURE__ */ React53.createElement(
11781
+ AlertDialogPrimitive.Cancel,
11782
+ __spreadValues({
11783
+ ref,
11784
+ className: cn(
11785
+ buttonVariants({ variant: "outline" }),
11786
+ "mt-2 sm:mt-0",
11787
+ className
11788
+ )
11789
+ }, props)
11790
+ );
11791
+ });
11792
+ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
11793
+
11794
+ // src/react-ui/components/elements/skeleton.tsx
11795
+ function Skeleton(_a) {
11796
+ var _b = _a, {
11797
+ className
11798
+ } = _b, props = __objRest(_b, [
11799
+ "className"
11800
+ ]);
11801
+ return /* @__PURE__ */ React.createElement(
11802
+ "div",
11803
+ __spreadValues({
11804
+ className: cn("animate-pulse rounded-md bg-muted", className)
11805
+ }, props)
11806
+ );
11807
+ }
11808
+ function SubjectManager() {
11809
+ const [subjects, setSubjects] = useState([]);
11810
+ const [isLoading, setIsLoading] = useState(true);
11811
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
11812
+ const [isAlertOpen, setIsAlertOpen] = useState(false);
11813
+ const [currentSubject, setCurrentSubject] = useState(null);
11814
+ const [subjectName, setSubjectName] = useState("");
11815
+ const [subjectCode, setSubjectCode] = useState("");
11816
+ const [itemToDelete, setItemToDelete] = useState(null);
11817
+ const [isPending, startTransition] = useTransition();
11818
+ const { toast: toast2 } = useToast();
11819
+ useEffect(() => {
11820
+ setIsLoading(true);
11821
+ try {
11822
+ const data = MetadataService.getSubjects();
11823
+ setSubjects(data);
11824
+ } catch (error) {
11825
+ toast2({ title: "Error", description: "Failed to fetch subjects from local storage.", variant: "destructive" });
11826
+ } finally {
11827
+ setIsLoading(false);
11828
+ }
11829
+ }, []);
11830
+ const refreshData = () => {
11831
+ setSubjects(MetadataService.getSubjects());
11832
+ };
11833
+ const handleAddItem = () => {
11834
+ setCurrentSubject(null);
11835
+ setSubjectName("");
11836
+ setSubjectCode("");
11837
+ setIsDialogOpen(true);
11838
+ };
11839
+ const handleEditItem = (subject) => {
11840
+ setCurrentSubject(subject);
11841
+ setSubjectName(subject.name);
11842
+ setSubjectCode(subject.code);
11843
+ setIsDialogOpen(true);
11844
+ };
11845
+ const handleDeleteItem = (subject) => {
11846
+ setItemToDelete(subject);
11847
+ setIsAlertOpen(true);
11848
+ };
11849
+ const confirmDelete = () => {
11850
+ if (!itemToDelete) return;
11851
+ startTransition(() => {
11852
+ try {
11853
+ MetadataService.deleteSubject(itemToDelete.code);
11854
+ toast2({ title: "Success", description: `Subject "${itemToDelete.name}" deleted.` });
11855
+ refreshData();
11856
+ } catch (error) {
11857
+ toast2({ title: "Error", description: error.message || "Failed to delete subject.", variant: "destructive" });
11858
+ } finally {
11859
+ setIsAlertOpen(false);
11860
+ setItemToDelete(null);
11861
+ }
11862
+ });
11863
+ };
11864
+ const handleSubmit = () => {
11865
+ if (!subjectName.trim() || !subjectCode.trim()) {
11866
+ toast2({ title: "Validation Error", description: "Please enter Subject Name and Subject Code.", variant: "destructive" });
11867
+ return;
11868
+ }
11869
+ startTransition(() => {
11870
+ try {
11871
+ if (currentSubject) {
11872
+ MetadataService.updateSubject(currentSubject.id, subjectName, subjectCode);
11873
+ toast2({ title: "Success", description: "Subject updated." });
11874
+ } else {
11875
+ MetadataService.addSubject(subjectName, subjectCode);
11876
+ toast2({ title: "Success", description: "Subject added." });
11877
+ }
11878
+ refreshData();
11879
+ setIsDialogOpen(false);
11880
+ } catch (error) {
11881
+ toast2({ title: "Error", description: error.message || "Failed to save subject.", variant: "destructive" });
11882
+ }
11883
+ });
11884
+ };
11885
+ return /* @__PURE__ */ React53__default.createElement(Card, null, /* @__PURE__ */ React53__default.createElement(CardHeader, null, /* @__PURE__ */ React53__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React53__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React53__default.createElement(BookCopy, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Subjects"), /* @__PURE__ */ React53__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React53__default.createElement(PlusCircle, { className: "mr-2 h-4 w-4" }), " Add Subject"))), /* @__PURE__ */ React53__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React53__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React53__default.createElement(Loader2, { className: "h-8 w-8 animate-spin text-primary" })) : subjects.length === 0 ? /* @__PURE__ */ React53__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No subjects found. Add one to get started!") : /* @__PURE__ */ React53__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React53__default.createElement(Table2, null, /* @__PURE__ */ React53__default.createElement(TableHeader, null, /* @__PURE__ */ React53__default.createElement(TableRow, null, /* @__PURE__ */ React53__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Created At"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Updated At"), /* @__PURE__ */ React53__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React53__default.createElement(TableBody, null, subjects.map((subject) => /* @__PURE__ */ React53__default.createElement(TableRow, { key: subject.id }, /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-mono text-xs" }, subject.code), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-medium" }, subject.name), /* @__PURE__ */ React53__default.createElement(TableCell, null, format(new Date(subject.createdAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React53__default.createElement(TableCell, null, format(new Date(subject.updatedAt), "dd/MM/yyyy HH:mm")), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(subject), className: "mr-2" }, /* @__PURE__ */ React53__default.createElement(Edit3, { className: "h-4 w-4" })), /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(subject), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React53__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React53__default.createElement(Dialog, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React53__default.createElement(DialogContent, { className: "sm:max-w-md" }, /* @__PURE__ */ React53__default.createElement(DialogHeader, null, /* @__PURE__ */ React53__default.createElement(DialogTitle, null, currentSubject ? "Edit Subject" : "Add New Subject"), /* @__PURE__ */ React53__default.createElement(DialogDescription, null, currentSubject ? "Update the details of the subject." : "Enter details for the new subject.")), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "subjectCode" }, "Subject Code"), /* @__PURE__ */ React53__default.createElement(
11886
+ Input,
11887
+ {
11888
+ id: "subjectCode",
11889
+ value: subjectCode,
11890
+ onChange: (e) => setSubjectCode(e.target.value.toUpperCase()),
11891
+ placeholder: "e.g., MATH",
11892
+ disabled: !!currentSubject
11893
+ }
11894
+ )), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "subjectName" }, "Subject Name"), /* @__PURE__ */ React53__default.createElement(
11895
+ Input,
11896
+ {
11897
+ id: "subjectName",
11898
+ value: subjectName,
11899
+ onChange: (e) => setSubjectName(e.target.value),
11900
+ placeholder: "e.g., Mathematics"
11901
+ }
11902
+ ))), /* @__PURE__ */ React53__default.createElement(DialogFooter, null, /* @__PURE__ */ React53__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !subjectName.trim() || !subjectCode.trim() }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Save")))), /* @__PURE__ */ React53__default.createElement(AlertDialog, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React53__default.createElement(AlertDialogContent, null, /* @__PURE__ */ React53__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React53__default.createElement(AlertDialogTitle, null, "Are you sure?"), /* @__PURE__ */ React53__default.createElement(AlertDialogDescription, null, 'This action cannot be undone. This will permanently delete the subject "', itemToDelete == null ? void 0 : itemToDelete.name, '" (Code: ', itemToDelete == null ? void 0 : itemToDelete.code, "). Ensure no topics, questions, or learning objectives reference this subject.")), /* @__PURE__ */ React53__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React53__default.createElement(AlertDialogCancel, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(AlertDialogAction, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Delete"))))));
11903
+ }
11904
+ function GradeLevelManager() {
11905
+ const [items, setItems] = useState([]);
11906
+ const [isLoading, setIsLoading] = useState(true);
11907
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
11908
+ const [isAlertOpen, setIsAlertOpen] = useState(false);
11909
+ const [currentItem, setCurrentItem] = useState(null);
11910
+ const [itemName, setItemName] = useState("");
11911
+ const [itemCode, setItemCode] = useState("");
11912
+ const [itemToDelete, setItemToDelete] = useState(null);
11913
+ const [isPending, startTransition] = useTransition();
11914
+ const { toast: toast2 } = useToast();
11915
+ useEffect(() => {
11916
+ fetchItems();
11917
+ }, []);
11918
+ const fetchItems = () => {
11919
+ setIsLoading(true);
11920
+ try {
11921
+ setItems(MetadataService.getGradeLevels());
11922
+ } catch (error) {
11923
+ toast2({ title: "Error", description: "Failed to fetch grade levels.", variant: "destructive" });
11924
+ } finally {
11925
+ setIsLoading(false);
11926
+ }
11927
+ };
11928
+ const handleAddItem = () => {
11929
+ setCurrentItem(null);
11930
+ setItemName("");
11931
+ setItemCode("");
11932
+ setIsDialogOpen(true);
11933
+ };
11934
+ const handleEditItem = (item) => {
11935
+ setCurrentItem(item);
11936
+ setItemName(item.name);
11937
+ setItemCode(item.code);
11938
+ setIsDialogOpen(true);
11939
+ };
11940
+ const handleDeleteItem = (item) => {
11941
+ setItemToDelete(item);
11942
+ setIsAlertOpen(true);
11943
+ };
11944
+ const confirmDelete = () => {
11945
+ if (!itemToDelete) return;
11946
+ startTransition(() => {
11947
+ try {
11948
+ MetadataService.deleteGradeLevel(itemToDelete.code);
11949
+ toast2({ title: "Success", description: `Grade Level "${itemToDelete.name}" deleted.` });
11950
+ fetchItems();
11951
+ } catch (error) {
11952
+ toast2({ title: "Error", description: "Failed to delete grade level.", variant: "destructive" });
11953
+ } finally {
11954
+ setIsAlertOpen(false);
11955
+ setItemToDelete(null);
11956
+ }
11957
+ });
11958
+ };
11959
+ const handleSubmit = () => {
11960
+ if (!itemName.trim() || !itemCode.trim()) {
11961
+ toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
11962
+ return;
11963
+ }
11964
+ startTransition(() => {
11965
+ try {
11966
+ if (currentItem) {
11967
+ MetadataService.updateGradeLevel(currentItem.id, itemName, itemCode);
11968
+ } else {
11969
+ MetadataService.addGradeLevel(itemName, itemCode);
11970
+ }
11971
+ toast2({ title: "Success", description: `Grade Level saved.` });
11972
+ fetchItems();
11973
+ setIsDialogOpen(false);
11974
+ } catch (error) {
11975
+ toast2({ title: "Error", description: "Failed to save grade level.", variant: "destructive" });
11976
+ }
11977
+ });
11978
+ };
11979
+ return /* @__PURE__ */ React53__default.createElement(Card, null, /* @__PURE__ */ React53__default.createElement(CardHeader, null, /* @__PURE__ */ React53__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React53__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React53__default.createElement(Award, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Grade Levels"), /* @__PURE__ */ React53__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React53__default.createElement(PlusCircle, { className: "mr-2 h-4 w-4" }), " Add Grade Level"))), /* @__PURE__ */ React53__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React53__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React53__default.createElement(Loader2, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React53__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No grade levels found.") : /* @__PURE__ */ React53__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React53__default.createElement(Table2, null, /* @__PURE__ */ React53__default.createElement(TableHeader, null, /* @__PURE__ */ React53__default.createElement(TableRow, null, /* @__PURE__ */ React53__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React53__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React53__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React53__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React53__default.createElement(Edit3, { className: "h-4 w-4" })), /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React53__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React53__default.createElement(Dialog, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React53__default.createElement(DialogContent, { className: "sm:max-w-md" }, /* @__PURE__ */ React53__default.createElement(DialogHeader, null, /* @__PURE__ */ React53__default.createElement(DialogTitle, null, currentItem ? "Edit Grade Level" : "Add New Grade Level")), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React53__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e) => setItemCode(e.target.value.toUpperCase()), placeholder: "e.g., G9", disabled: !!currentItem })), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React53__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e) => setItemName(e.target.value), placeholder: "e.g., Grade 9" }))), /* @__PURE__ */ React53__default.createElement(DialogFooter, null, /* @__PURE__ */ React53__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Save")))), /* @__PURE__ */ React53__default.createElement(AlertDialog, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React53__default.createElement(AlertDialogContent, null, /* @__PURE__ */ React53__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React53__default.createElement(AlertDialogTitle, null, "Are you sure?"), /* @__PURE__ */ React53__default.createElement(AlertDialogDescription, null, 'This will permanently delete "', itemToDelete == null ? void 0 : itemToDelete.name, '".')), /* @__PURE__ */ React53__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React53__default.createElement(AlertDialogCancel, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(AlertDialogAction, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Delete"))))));
11980
+ }
11981
+ function TopicManager() {
11982
+ const [topics, setTopics] = useState([]);
11983
+ const [subjects, setSubjects] = useState([]);
11984
+ const [isLoading, setIsLoading] = useState(true);
11985
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
11986
+ const [isAlertOpen, setIsAlertOpen] = useState(false);
11987
+ const [currentItem, setCurrentItem] = useState(null);
11988
+ const [itemName, setItemName] = useState("");
11989
+ const [itemCode, setItemCode] = useState("");
11990
+ const [selectedSubjectCode, setSelectedSubjectCode] = useState("");
11991
+ const [itemToDelete, setItemToDelete] = useState(null);
11992
+ const [isPending, startTransition] = useTransition();
11993
+ const { toast: toast2 } = useToast();
11994
+ const fetchData = () => {
11995
+ setIsLoading(true);
11996
+ try {
11997
+ const topicsData = MetadataService.getTopics();
11998
+ const subjectsData = MetadataService.getSubjects();
11999
+ setTopics(topicsData);
12000
+ setSubjects(subjectsData);
12001
+ if (subjectsData.length > 0 && !selectedSubjectCode) {
12002
+ setSelectedSubjectCode(subjectsData[0].code);
12003
+ }
12004
+ } catch (error) {
12005
+ toast2({
12006
+ title: "Error",
12007
+ description: "Failed to fetch topics or subjects.",
12008
+ variant: "destructive"
12009
+ });
12010
+ } finally {
12011
+ setIsLoading(false);
12012
+ }
12013
+ };
12014
+ useEffect(() => {
12015
+ fetchData();
12016
+ }, []);
12017
+ const handleAddItem = () => {
12018
+ setCurrentItem(null);
12019
+ setItemName("");
12020
+ setItemCode("");
12021
+ if (subjects.length > 0) {
12022
+ setSelectedSubjectCode(subjects[0].code);
12023
+ }
12024
+ setIsDialogOpen(true);
12025
+ };
12026
+ const handleEditItem = (topic) => {
12027
+ setCurrentItem(topic);
12028
+ setItemName(topic.name);
12029
+ setItemCode(topic.code);
12030
+ setSelectedSubjectCode(topic.subjectCode);
12031
+ setIsDialogOpen(true);
12032
+ };
12033
+ const handleDeleteItem = (topic) => {
12034
+ setItemToDelete(topic);
12035
+ setIsAlertOpen(true);
12036
+ };
12037
+ const confirmDelete = () => {
12038
+ if (!itemToDelete) return;
12039
+ startTransition(() => {
12040
+ try {
12041
+ MetadataService.deleteTopic(itemToDelete.code);
12042
+ toast2({
12043
+ title: "Success",
12044
+ description: `Topic "${itemToDelete.name}" deleted.`
12045
+ });
12046
+ fetchData();
12047
+ } catch (error) {
12048
+ toast2({
12049
+ title: "Error",
12050
+ description: "Failed to delete topic.",
12051
+ variant: "destructive"
12052
+ });
12053
+ } finally {
12054
+ setIsAlertOpen(false);
12055
+ setItemToDelete(null);
12056
+ }
12057
+ });
12058
+ };
12059
+ const handleSubmit = () => {
12060
+ if (!itemName.trim() || !itemCode.trim() || !selectedSubjectCode) {
12061
+ toast2({
12062
+ title: "Validation Error",
12063
+ description: "Name, Code, and Subject are required.",
12064
+ variant: "destructive"
12065
+ });
12066
+ return;
12067
+ }
12068
+ startTransition(() => {
12069
+ try {
12070
+ if (currentItem) {
12071
+ MetadataService.updateTopic(
12072
+ currentItem.id,
12073
+ itemName,
12074
+ itemCode,
12075
+ selectedSubjectCode
12076
+ );
12077
+ } else {
12078
+ MetadataService.addTopic(
12079
+ itemName,
12080
+ itemCode,
12081
+ selectedSubjectCode
12082
+ );
12083
+ }
12084
+ toast2({ title: "Success", description: "Topic saved." });
12085
+ fetchData();
12086
+ setIsDialogOpen(false);
12087
+ } catch (error) {
12088
+ toast2({
12089
+ title: "Error",
12090
+ description: "Failed to save topic.",
12091
+ variant: "destructive"
12092
+ });
12093
+ }
12094
+ });
12095
+ };
12096
+ const getSubjectName = (subjectCode) => {
12097
+ var _a;
12098
+ return ((_a = subjects.find((s) => s.code === subjectCode)) == null ? void 0 : _a.name) || "N/A";
12099
+ };
12100
+ return /* @__PURE__ */ React53__default.createElement(Card, null, /* @__PURE__ */ React53__default.createElement(CardHeader, null, /* @__PURE__ */ React53__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React53__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React53__default.createElement(Tag, { className: "mr-2 h-5 w-5 text-primary" }), "Manage Topics"), /* @__PURE__ */ React53__default.createElement(
12101
+ Button,
12102
+ {
12103
+ onClick: handleAddItem,
12104
+ size: "sm",
12105
+ disabled: subjects.length === 0
12106
+ },
12107
+ /* @__PURE__ */ React53__default.createElement(PlusCircle, { className: "mr-2 h-4 w-4" }),
12108
+ " Add Topic"
12109
+ )), subjects.length === 0 && !isLoading && /* @__PURE__ */ React53__default.createElement("p", { className: "text-sm text-destructive" }, "Please add subjects before adding topics.")), /* @__PURE__ */ React53__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React53__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React53__default.createElement(Loader2, { className: "h-8 w-8 animate-spin text-primary" })) : topics.length === 0 ? /* @__PURE__ */ React53__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No topics found. Add one to get started!") : /* @__PURE__ */ React53__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React53__default.createElement(Table2, null, /* @__PURE__ */ React53__default.createElement(TableHeader, null, /* @__PURE__ */ React53__default.createElement(TableRow, null, /* @__PURE__ */ React53__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React53__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React53__default.createElement(TableBody, null, topics.map((topic) => /* @__PURE__ */ React53__default.createElement(TableRow, { key: topic.id }, /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-mono text-xs" }, topic.code), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-medium" }, topic.name), /* @__PURE__ */ React53__default.createElement(TableCell, null, getSubjectName(topic.subjectCode), " ", "(", topic.subjectCode, ")"), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React53__default.createElement(
12110
+ Button,
12111
+ {
12112
+ variant: "ghost",
12113
+ size: "icon",
12114
+ onClick: () => handleEditItem(topic),
12115
+ className: "mr-2"
12116
+ },
12117
+ /* @__PURE__ */ React53__default.createElement(Edit3, { className: "h-4 w-4" })
12118
+ ), /* @__PURE__ */ React53__default.createElement(
12119
+ Button,
12120
+ {
12121
+ variant: "ghost",
12122
+ size: "icon",
12123
+ onClick: () => handleDeleteItem(topic),
12124
+ className: "text-destructive hover:text-destructive"
12125
+ },
12126
+ /* @__PURE__ */ React53__default.createElement(Trash2, { className: "h-4 w-4" })
12127
+ ))))))), /* @__PURE__ */ React53__default.createElement(Dialog, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React53__default.createElement(DialogContent, { className: "sm:max-w-md" }, /* @__PURE__ */ React53__default.createElement(DialogHeader, null, /* @__PURE__ */ React53__default.createElement(DialogTitle, null, currentItem ? "Edit Topic" : "Add New Topic")), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemCode" }, "Topic Code"), /* @__PURE__ */ React53__default.createElement(
12128
+ Input,
12129
+ {
12130
+ id: "itemCode",
12131
+ value: itemCode,
12132
+ onChange: (e) => setItemCode(
12133
+ e.target.value.toUpperCase()
12134
+ ),
12135
+ placeholder: "e.g., ALG-BASICS",
12136
+ disabled: !!currentItem
12137
+ }
12138
+ )), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemName" }, "Topic Name"), /* @__PURE__ */ React53__default.createElement(
12139
+ Input,
12140
+ {
12141
+ id: "itemName",
12142
+ value: itemName,
12143
+ onChange: (e) => setItemName(e.target.value),
12144
+ placeholder: "e.g., Algebra Basics"
12145
+ }
12146
+ )), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "subjectCode" }, "Subject"), /* @__PURE__ */ React53__default.createElement(
12147
+ Select,
12148
+ {
12149
+ value: selectedSubjectCode,
12150
+ onValueChange: setSelectedSubjectCode,
12151
+ disabled: subjects.length === 0
12152
+ },
12153
+ /* @__PURE__ */ React53__default.createElement(SelectTrigger, { id: "subjectCode" }, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Select a subject" })),
12154
+ /* @__PURE__ */ React53__default.createElement(SelectContent, null, subjects.map((subject) => /* @__PURE__ */ React53__default.createElement(
12155
+ SelectItem,
12156
+ {
12157
+ key: subject.code,
12158
+ value: subject.code
12159
+ },
12160
+ subject.name,
12161
+ " (",
12162
+ subject.code,
12163
+ ")"
12164
+ )))
12165
+ ))), /* @__PURE__ */ React53__default.createElement(DialogFooter, null, /* @__PURE__ */ React53__default.createElement(
12166
+ Button,
12167
+ {
12168
+ type: "button",
12169
+ variant: "outline",
12170
+ onClick: () => setIsDialogOpen(false),
12171
+ disabled: isPending
12172
+ },
12173
+ "Cancel"
12174
+ ), /* @__PURE__ */ React53__default.createElement(
12175
+ Button,
12176
+ {
12177
+ type: "submit",
12178
+ onClick: handleSubmit,
12179
+ disabled: isPending || !itemName.trim() || !itemCode.trim() || !selectedSubjectCode
12180
+ },
12181
+ isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
12182
+ " ",
12183
+ "Save"
12184
+ )))), /* @__PURE__ */ React53__default.createElement(AlertDialog, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React53__default.createElement(AlertDialogContent, null, /* @__PURE__ */ React53__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React53__default.createElement(AlertDialogTitle, null, "Are you sure?"), /* @__PURE__ */ React53__default.createElement(AlertDialogDescription, null, 'This will permanently delete topic "', itemToDelete == null ? void 0 : itemToDelete.name, '".')), /* @__PURE__ */ React53__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React53__default.createElement(AlertDialogCancel, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(
12185
+ AlertDialogAction,
12186
+ {
12187
+ onClick: confirmDelete,
12188
+ disabled: isPending,
12189
+ className: "bg-destructive hover:bg-destructive/90"
12190
+ },
12191
+ isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
12192
+ " ",
12193
+ "Delete"
12194
+ ))))));
12195
+ }
12196
+ function CategoryManager() {
12197
+ const [items, setItems] = useState([]);
12198
+ const [isLoading, setIsLoading] = useState(true);
12199
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
12200
+ const [isAlertOpen, setIsAlertOpen] = useState(false);
12201
+ const [currentItem, setCurrentItem] = useState(null);
12202
+ const [itemName, setItemName] = useState("");
12203
+ const [itemCode, setItemCode] = useState("");
12204
+ const [itemDescription, setItemDescription] = useState("");
12205
+ const [itemToDelete, setItemToDelete] = useState(null);
12206
+ const [isPending, startTransition] = useTransition();
12207
+ const { toast: toast2 } = useToast();
12208
+ useEffect(() => {
12209
+ fetchItems();
12210
+ }, []);
12211
+ const fetchItems = () => {
12212
+ setIsLoading(true);
12213
+ try {
12214
+ setItems(MetadataService.getCategories());
12215
+ } catch (error) {
12216
+ toast2({ title: "Error", description: "Failed to fetch categories.", variant: "destructive" });
12217
+ } finally {
12218
+ setIsLoading(false);
12219
+ }
12220
+ };
12221
+ const handleAddItem = () => {
12222
+ setCurrentItem(null);
12223
+ setItemName("");
12224
+ setItemCode("");
12225
+ setItemDescription("");
12226
+ setIsDialogOpen(true);
12227
+ };
12228
+ const handleEditItem = (item) => {
12229
+ setCurrentItem(item);
12230
+ setItemName(item.name);
12231
+ setItemCode(item.code);
12232
+ setItemDescription(item.description || "");
12233
+ setIsDialogOpen(true);
12234
+ };
12235
+ const handleDeleteItem = (item) => {
12236
+ setItemToDelete(item);
12237
+ setIsAlertOpen(true);
12238
+ };
12239
+ const confirmDelete = () => {
12240
+ if (!itemToDelete) return;
12241
+ startTransition(() => {
12242
+ try {
12243
+ MetadataService.deleteCategory(itemToDelete.code);
12244
+ toast2({ title: "Success", description: `Category "${itemToDelete.name}" deleted.` });
12245
+ fetchItems();
12246
+ } catch (error) {
12247
+ toast2({ title: "Error", description: "Failed to delete category.", variant: "destructive" });
12248
+ } finally {
12249
+ setIsAlertOpen(false);
12250
+ setItemToDelete(null);
12251
+ }
12252
+ });
12253
+ };
12254
+ const handleSubmit = () => {
12255
+ if (!itemName.trim() || !itemCode.trim()) {
12256
+ toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
12257
+ return;
12258
+ }
12259
+ startTransition(() => {
12260
+ try {
12261
+ if (currentItem) {
12262
+ MetadataService.updateCategory(currentItem.id, itemName, itemCode, itemDescription);
12263
+ } else {
12264
+ MetadataService.addCategory(itemName, itemCode, itemDescription);
12265
+ }
12266
+ toast2({ title: "Success", description: "Category saved." });
12267
+ fetchItems();
12268
+ setIsDialogOpen(false);
12269
+ } catch (error) {
12270
+ toast2({ title: "Error", description: "Failed to save category.", variant: "destructive" });
12271
+ }
12272
+ });
12273
+ };
12274
+ return /* @__PURE__ */ React53__default.createElement(Card, null, /* @__PURE__ */ React53__default.createElement(CardHeader, null, /* @__PURE__ */ React53__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React53__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React53__default.createElement(Layers, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Categories"), /* @__PURE__ */ React53__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React53__default.createElement(PlusCircle, { className: "mr-2 h-4 w-4" }), " Add Category"))), /* @__PURE__ */ React53__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React53__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React53__default.createElement(Loader2, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React53__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No categories found.") : /* @__PURE__ */ React53__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React53__default.createElement(Table2, null, /* @__PURE__ */ React53__default.createElement(TableHeader, null, /* @__PURE__ */ React53__default.createElement(TableRow, null, /* @__PURE__ */ React53__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React53__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React53__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React53__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React53__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React53__default.createElement(Edit3, { className: "h-4 w-4" })), /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React53__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React53__default.createElement(Dialog, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React53__default.createElement(DialogContent, { className: "sm:max-w-md" }, /* @__PURE__ */ React53__default.createElement(DialogHeader, null, /* @__PURE__ */ React53__default.createElement(DialogTitle, null, currentItem ? "Edit Category" : "Add New Category")), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React53__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e) => setItemCode(e.target.value.toUpperCase()), placeholder: "e.g., CORE_CONCEPT", disabled: !!currentItem })), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React53__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e) => setItemName(e.target.value), placeholder: "e.g., Core Concept" })), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React53__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e) => setItemDescription(e.target.value), placeholder: "e.g., Fundamental ideas within a subject." }))), /* @__PURE__ */ React53__default.createElement(DialogFooter, null, /* @__PURE__ */ React53__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemName.trim() || !itemCode.trim() }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Save")))), /* @__PURE__ */ React53__default.createElement(AlertDialog, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React53__default.createElement(AlertDialogContent, null, /* @__PURE__ */ React53__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React53__default.createElement(AlertDialogTitle, null, "Are you sure?"), /* @__PURE__ */ React53__default.createElement(AlertDialogDescription, null, 'This will permanently delete "', itemToDelete == null ? void 0 : itemToDelete.name, '".')), /* @__PURE__ */ React53__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React53__default.createElement(AlertDialogCancel, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(AlertDialogAction, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Delete"))))));
12275
+ }
12276
+ function BloomLevelManager() {
12277
+ const [items, setItems] = useState([]);
12278
+ const [isLoading, setIsLoading] = useState(true);
12279
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
12280
+ const [isAlertOpen, setIsAlertOpen] = useState(false);
12281
+ const [currentItem, setCurrentItem] = useState(null);
12282
+ const [itemCode, setItemCode] = useState("");
12283
+ const [itemName, setItemName] = useState("");
12284
+ const [itemDescription, setItemDescription] = useState("");
12285
+ const [itemToDelete, setItemToDelete] = useState(null);
12286
+ const [isPending, startTransition] = useTransition();
12287
+ const { toast: toast2 } = useToast();
12288
+ useEffect(() => {
12289
+ fetchItems();
12290
+ }, []);
12291
+ const fetchItems = () => {
12292
+ setIsLoading(true);
12293
+ try {
12294
+ setItems(MetadataService.getBloomLevels());
12295
+ } catch (error) {
12296
+ toast2({ title: "Error", description: "Failed to fetch Bloom's Levels.", variant: "destructive" });
12297
+ } finally {
12298
+ setIsLoading(false);
12299
+ }
12300
+ };
12301
+ const handleAddItem = () => {
12302
+ setCurrentItem(null);
12303
+ setItemCode("");
12304
+ setItemName("");
12305
+ setItemDescription("");
12306
+ setIsDialogOpen(true);
12307
+ };
12308
+ const handleEditItem = (item) => {
12309
+ setCurrentItem(item);
12310
+ setItemCode(item.code);
12311
+ setItemName(item.name);
12312
+ setItemDescription(item.description || "");
12313
+ setIsDialogOpen(true);
12314
+ };
12315
+ const handleDeleteItem = (item) => {
12316
+ setItemToDelete(item);
12317
+ setIsAlertOpen(true);
12318
+ };
12319
+ const confirmDelete = () => {
12320
+ if (!itemToDelete) return;
12321
+ startTransition(() => {
12322
+ try {
12323
+ MetadataService.deleteBloomLevel(itemToDelete.code);
12324
+ toast2({ title: "Success", description: `Bloom's Level "${itemToDelete.name}" deleted.` });
12325
+ fetchItems();
12326
+ } catch (error) {
12327
+ toast2({ title: "Error", description: "Failed to delete Bloom's Level.", variant: "destructive" });
12328
+ } finally {
12329
+ setIsAlertOpen(false);
12330
+ setItemToDelete(null);
12331
+ }
12332
+ });
12333
+ };
12334
+ const handleSubmit = () => {
12335
+ if (!itemName.trim() || !itemCode.trim()) {
12336
+ toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
12337
+ return;
12338
+ }
12339
+ startTransition(() => {
12340
+ try {
12341
+ if (currentItem) {
12342
+ MetadataService.updateBloomLevel(currentItem.id, itemName, itemCode, itemDescription);
12343
+ } else {
12344
+ MetadataService.addBloomLevel(itemName, itemCode, itemDescription);
12345
+ }
12346
+ toast2({ title: "Success", description: "Bloom's Level saved." });
12347
+ fetchItems();
12348
+ setIsDialogOpen(false);
12349
+ } catch (error) {
12350
+ toast2({ title: "Error", description: "Failed to save Bloom's Level.", variant: "destructive" });
12351
+ }
12352
+ });
12353
+ };
12354
+ return /* @__PURE__ */ React53__default.createElement(Card, null, /* @__PURE__ */ React53__default.createElement(CardHeader, null, /* @__PURE__ */ React53__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React53__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React53__default.createElement(Brain, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Bloom's Levels"), /* @__PURE__ */ React53__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React53__default.createElement(PlusCircle, { className: "mr-2 h-4 w-4" }), " Add Bloom's Level"))), /* @__PURE__ */ React53__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React53__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React53__default.createElement(Loader2, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React53__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Bloom's Levels found.") : /* @__PURE__ */ React53__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React53__default.createElement(Table2, null, /* @__PURE__ */ React53__default.createElement(TableHeader, null, /* @__PURE__ */ React53__default.createElement(TableRow, null, /* @__PURE__ */ React53__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React53__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React53__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React53__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React53__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React53__default.createElement(Edit3, { className: "h-4 w-4" })), /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React53__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React53__default.createElement(Dialog, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React53__default.createElement(DialogContent, { className: "sm:max-w-md" }, /* @__PURE__ */ React53__default.createElement(DialogHeader, null, /* @__PURE__ */ React53__default.createElement(DialogTitle, null, currentItem ? "Edit Bloom's Level" : "Add New Bloom's Level")), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React53__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e) => setItemCode(e.target.value.toUpperCase()), placeholder: "e.g., REMEMBER", disabled: !!currentItem })), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React53__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e) => setItemName(e.target.value), placeholder: "e.g., Remembering" })), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React53__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e) => setItemDescription(e.target.value), placeholder: "e.g., Recall facts and basic concepts." }))), /* @__PURE__ */ React53__default.createElement(DialogFooter, null, /* @__PURE__ */ React53__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Save")))), /* @__PURE__ */ React53__default.createElement(AlertDialog, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React53__default.createElement(AlertDialogContent, null, /* @__PURE__ */ React53__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React53__default.createElement(AlertDialogTitle, null, "Are you sure?"), /* @__PURE__ */ React53__default.createElement(AlertDialogDescription, null, 'This will permanently delete "', itemToDelete == null ? void 0 : itemToDelete.name, '".')), /* @__PURE__ */ React53__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React53__default.createElement(AlertDialogCancel, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(AlertDialogAction, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Delete"))))));
12355
+ }
12356
+ function QuestionTypeManager() {
12357
+ const [items, setItems] = useState([]);
12358
+ const [isLoading, setIsLoading] = useState(true);
12359
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
12360
+ const [isAlertOpen, setIsAlertOpen] = useState(false);
12361
+ const [currentItem, setCurrentItem] = useState(null);
12362
+ const [itemCode, setItemCode] = useState("");
12363
+ const [itemName, setItemName] = useState("");
12364
+ const [itemDescription, setItemDescription] = useState("");
12365
+ const [itemToDelete, setItemToDelete] = useState(null);
12366
+ const [isPending, startTransition] = useTransition();
12367
+ const { toast: toast2 } = useToast();
12368
+ useEffect(() => {
12369
+ fetchItems();
12370
+ }, []);
12371
+ const fetchItems = () => {
12372
+ setIsLoading(true);
12373
+ try {
12374
+ setItems(MetadataService.getQuestionTypes());
12375
+ } catch (error) {
12376
+ toast2({ title: "Error", description: "Failed to fetch Question Types.", variant: "destructive" });
12377
+ } finally {
12378
+ setIsLoading(false);
12379
+ }
12380
+ };
12381
+ const handleAddItem = () => {
12382
+ setCurrentItem(null);
12383
+ setItemCode("");
12384
+ setItemName("");
12385
+ setItemDescription("");
12386
+ setIsDialogOpen(true);
12387
+ };
12388
+ const handleEditItem = (item) => {
12389
+ setCurrentItem(item);
12390
+ setItemCode(item.code);
12391
+ setItemName(item.name);
12392
+ setItemDescription(item.description || "");
12393
+ setIsDialogOpen(true);
12394
+ };
12395
+ const handleDeleteItem = (item) => {
12396
+ setItemToDelete(item);
12397
+ setIsAlertOpen(true);
12398
+ };
12399
+ const confirmDelete = () => {
12400
+ if (!itemToDelete) return;
12401
+ startTransition(() => {
12402
+ try {
12403
+ MetadataService.deleteQuestionType(itemToDelete.code);
12404
+ toast2({ title: "Success", description: `Question Type "${itemToDelete.name}" deleted.` });
12405
+ fetchItems();
12406
+ } catch (error) {
12407
+ toast2({ title: "Error", description: "Failed to delete Question Type.", variant: "destructive" });
12408
+ } finally {
12409
+ setIsAlertOpen(false);
12410
+ setItemToDelete(null);
12411
+ }
12412
+ });
12413
+ };
12414
+ const handleSubmit = () => {
12415
+ if (!itemName.trim() || !itemCode.trim()) {
12416
+ toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
12417
+ return;
12418
+ }
12419
+ startTransition(() => {
12420
+ try {
12421
+ if (currentItem) {
12422
+ MetadataService.updateQuestionType(currentItem.id, itemName, itemCode, itemDescription);
12423
+ } else {
12424
+ MetadataService.addQuestionType(itemName, itemCode, itemDescription);
12425
+ }
12426
+ toast2({ title: "Success", description: "Question Type saved." });
12427
+ fetchItems();
12428
+ setIsDialogOpen(false);
12429
+ } catch (error) {
12430
+ toast2({ title: "Error", description: "Failed to save Question Type.", variant: "destructive" });
12431
+ }
12432
+ });
12433
+ };
12434
+ return /* @__PURE__ */ React53__default.createElement(Card, null, /* @__PURE__ */ React53__default.createElement(CardHeader, null, /* @__PURE__ */ React53__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React53__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React53__default.createElement(HelpCircle, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Question Types"), /* @__PURE__ */ React53__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React53__default.createElement(PlusCircle, { className: "mr-2 h-4 w-4" }), " Add Question Type"))), /* @__PURE__ */ React53__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React53__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React53__default.createElement(Loader2, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React53__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Question Types found.") : /* @__PURE__ */ React53__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React53__default.createElement(Table2, null, /* @__PURE__ */ React53__default.createElement(TableHeader, null, /* @__PURE__ */ React53__default.createElement(TableRow, null, /* @__PURE__ */ React53__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React53__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React53__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React53__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React53__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React53__default.createElement(Edit3, { className: "h-4 w-4" })), /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React53__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React53__default.createElement(Dialog, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React53__default.createElement(DialogContent, { className: "sm:max-w-md" }, /* @__PURE__ */ React53__default.createElement(DialogHeader, null, /* @__PURE__ */ React53__default.createElement(DialogTitle, null, currentItem ? "Edit Question Type" : "Add New Question Type")), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React53__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e) => setItemCode(e.target.value.toUpperCase()), placeholder: "e.g., MCQ", disabled: !!currentItem })), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React53__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e) => setItemName(e.target.value), placeholder: "e.g., Multiple Choice" })), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React53__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e) => setItemDescription(e.target.value), placeholder: "e.g., Select one answer from a list of options." }))), /* @__PURE__ */ React53__default.createElement(DialogFooter, null, /* @__PURE__ */ React53__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Save")))), /* @__PURE__ */ React53__default.createElement(AlertDialog, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React53__default.createElement(AlertDialogContent, null, /* @__PURE__ */ React53__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React53__default.createElement(AlertDialogTitle, null, "Are you sure?"), /* @__PURE__ */ React53__default.createElement(AlertDialogDescription, null, 'This will permanently delete "', itemToDelete == null ? void 0 : itemToDelete.name, '".')), /* @__PURE__ */ React53__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React53__default.createElement(AlertDialogCancel, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(AlertDialogAction, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Delete"))))));
12435
+ }
12436
+ function LearningObjectiveManager() {
12437
+ const [items, setItems] = useState([]);
12438
+ const [subjects, setSubjects] = useState([]);
12439
+ const [isLoading, setIsLoading] = useState(true);
12440
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
12441
+ const [isAlertOpen, setIsAlertOpen] = useState(false);
12442
+ const [currentItem, setCurrentItem] = useState(null);
12443
+ const [itemName, setItemName] = useState("");
12444
+ const [itemCode, setItemCode] = useState("");
12445
+ const [itemDescription, setItemDescription] = useState("");
12446
+ const [selectedSubjectCode, setSelectedSubjectCode] = useState(void 0);
12447
+ const [itemToDelete, setItemToDelete] = useState(null);
12448
+ const [isPending, startTransition] = useTransition();
12449
+ const { toast: toast2 } = useToast();
12450
+ const fetchData = () => {
12451
+ setIsLoading(true);
12452
+ try {
12453
+ const loData = MetadataService.getLearningObjectives();
12454
+ const subjectsData = MetadataService.getSubjects();
12455
+ setItems(loData);
12456
+ setSubjects(subjectsData);
12457
+ } catch (error) {
12458
+ toast2({
12459
+ title: "Error",
12460
+ description: "Failed to fetch Learning Objectives or Subjects.",
12461
+ variant: "destructive"
12462
+ });
12463
+ } finally {
12464
+ setIsLoading(false);
12465
+ }
12466
+ };
12467
+ useEffect(() => {
12468
+ fetchData();
12469
+ }, []);
12470
+ const handleAddItem = () => {
12471
+ setCurrentItem(null);
12472
+ setItemName("");
12473
+ setItemCode("");
12474
+ setItemDescription("");
12475
+ setSelectedSubjectCode(
12476
+ subjects.length > 0 ? subjects[0].code : void 0
12477
+ );
12478
+ setIsDialogOpen(true);
12479
+ };
12480
+ const handleEditItem = (item) => {
12481
+ setCurrentItem(item);
12482
+ setItemName(item.name);
12483
+ setItemCode(item.code);
12484
+ setItemDescription(item.description || "");
12485
+ setSelectedSubjectCode(item.subjectCode);
12486
+ setIsDialogOpen(true);
12487
+ };
12488
+ const handleDeleteItem = (item) => {
12489
+ setItemToDelete(item);
12490
+ setIsAlertOpen(true);
12491
+ };
12492
+ const confirmDelete = () => {
12493
+ if (!itemToDelete) return;
12494
+ startTransition(() => {
12495
+ try {
12496
+ MetadataService.deleteLearningObjective(itemToDelete.code);
12497
+ toast2({
12498
+ title: "Success",
12499
+ description: `Learning Objective "${itemToDelete.name}" deleted.`
12500
+ });
12501
+ fetchData();
12502
+ } catch (error) {
12503
+ toast2({
12504
+ title: "Error",
12505
+ description: "Failed to delete Learning Objective.",
12506
+ variant: "destructive"
12507
+ });
12508
+ } finally {
12509
+ setIsAlertOpen(false);
12510
+ setItemToDelete(null);
12511
+ }
12512
+ });
12513
+ };
12514
+ const handleSubmit = () => {
12515
+ if (!itemName.trim() || !itemCode.trim()) {
12516
+ toast2({
12517
+ title: "Validation Error",
12518
+ description: "Code and Name are required.",
12519
+ variant: "destructive"
12520
+ });
12521
+ return;
12522
+ }
12523
+ startTransition(() => {
12524
+ try {
12525
+ if (currentItem) {
12526
+ MetadataService.updateLearningObjective(
12527
+ currentItem.id,
12528
+ itemName,
12529
+ itemCode,
12530
+ selectedSubjectCode,
12531
+ itemDescription
12532
+ );
12533
+ } else {
12534
+ MetadataService.addLearningObjective(
12535
+ itemName,
12536
+ itemCode,
12537
+ selectedSubjectCode,
12538
+ itemDescription
12539
+ );
12540
+ }
12541
+ toast2({
12542
+ title: "Success",
12543
+ description: "Learning Objective saved."
12544
+ });
12545
+ fetchData();
12546
+ setIsDialogOpen(false);
12547
+ } catch (error) {
12548
+ toast2({
12549
+ title: "Error",
12550
+ description: "Failed to save Learning Objective.",
12551
+ variant: "destructive"
12552
+ });
12553
+ }
12554
+ });
12555
+ };
12556
+ const getSubjectName = (subjectCode) => {
12557
+ var _a;
12558
+ if (!subjectCode) return "N/A";
12559
+ return ((_a = subjects.find((s) => s.code === subjectCode)) == null ? void 0 : _a.name) || "N/A";
12560
+ };
12561
+ return /* @__PURE__ */ React53__default.createElement(Card, null, /* @__PURE__ */ React53__default.createElement(CardHeader, null, /* @__PURE__ */ React53__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React53__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React53__default.createElement(Lightbulb, { className: "mr-2 h-5 w-5 text-primary" }), " ", "Manage Learning Objectives"), /* @__PURE__ */ React53__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React53__default.createElement(PlusCircle, { className: "mr-2 h-4 w-4" }), " Add Learning Objective"))), /* @__PURE__ */ React53__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React53__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React53__default.createElement(Loader2, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React53__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Learning Objectives found.") : /* @__PURE__ */ React53__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React53__default.createElement(Table2, null, /* @__PURE__ */ React53__default.createElement(TableHeader, null, /* @__PURE__ */ React53__default.createElement(TableRow, null, /* @__PURE__ */ React53__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Subject"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React53__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React53__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React53__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React53__default.createElement(TableCell, null, getSubjectName(item.subjectCode)), /* @__PURE__ */ React53__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React53__default.createElement(
12562
+ Button,
12563
+ {
12564
+ variant: "ghost",
12565
+ size: "icon",
12566
+ onClick: () => handleEditItem(item),
12567
+ className: "mr-2"
12568
+ },
12569
+ /* @__PURE__ */ React53__default.createElement(Edit3, { className: "h-4 w-4" })
12570
+ ), /* @__PURE__ */ React53__default.createElement(
12571
+ Button,
12572
+ {
12573
+ variant: "ghost",
12574
+ size: "icon",
12575
+ onClick: () => handleDeleteItem(item),
12576
+ className: "text-destructive hover:text-destructive"
12577
+ },
12578
+ /* @__PURE__ */ React53__default.createElement(Trash2, { className: "h-4 w-4" })
12579
+ ))))))), /* @__PURE__ */ React53__default.createElement(Dialog, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React53__default.createElement(DialogContent, { className: "sm:max-w-md" }, /* @__PURE__ */ React53__default.createElement(DialogHeader, null, /* @__PURE__ */ React53__default.createElement(DialogTitle, null, currentItem ? "Edit Learning Objective" : "Add New Learning Objective")), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React53__default.createElement(
12580
+ Input,
12581
+ {
12582
+ id: "itemCode",
12583
+ value: itemCode,
12584
+ onChange: (e) => setItemCode(
12585
+ e.target.value.toUpperCase()
12586
+ ),
12587
+ disabled: !!currentItem
12588
+ }
12589
+ )), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React53__default.createElement(
12590
+ Input,
12591
+ {
12592
+ id: "itemName",
12593
+ value: itemName,
12594
+ onChange: (e) => setItemName(e.target.value)
12595
+ }
12596
+ )), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemSubject" }, "Subject (Optional)"), /* @__PURE__ */ React53__default.createElement(
12597
+ Select,
12598
+ {
12599
+ value: selectedSubjectCode || "",
12600
+ onValueChange: (value) => setSelectedSubjectCode(
12601
+ value === "_NONE_" ? void 0 : value
12602
+ )
12603
+ },
12604
+ /* @__PURE__ */ React53__default.createElement(SelectTrigger, { id: "itemSubject" }, /* @__PURE__ */ React53__default.createElement(SelectValue, { placeholder: "Select a subject" })),
12605
+ /* @__PURE__ */ React53__default.createElement(SelectContent, null, /* @__PURE__ */ React53__default.createElement(SelectItem, { value: "_NONE_" }, "No Specific Subject"), subjects.map((subject) => /* @__PURE__ */ React53__default.createElement(
12606
+ SelectItem,
12607
+ {
12608
+ key: subject.id,
12609
+ value: subject.code
12610
+ },
12611
+ subject.name
12612
+ )))
12613
+ )), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React53__default.createElement(
12614
+ Textarea,
12615
+ {
12616
+ id: "itemDescription",
12617
+ value: itemDescription,
12618
+ onChange: (e) => setItemDescription(e.target.value)
12619
+ }
12620
+ ))), /* @__PURE__ */ React53__default.createElement(DialogFooter, null, /* @__PURE__ */ React53__default.createElement(
12621
+ Button,
12622
+ {
12623
+ type: "button",
12624
+ variant: "outline",
12625
+ onClick: () => setIsDialogOpen(false),
12626
+ disabled: isPending
12627
+ },
12628
+ "Cancel"
12629
+ ), /* @__PURE__ */ React53__default.createElement(
12630
+ Button,
12631
+ {
12632
+ type: "submit",
12633
+ onClick: handleSubmit,
12634
+ disabled: isPending || !itemName.trim() || !itemCode.trim()
12635
+ },
12636
+ isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
12637
+ " ",
12638
+ "Save"
12639
+ )))), /* @__PURE__ */ React53__default.createElement(AlertDialog, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React53__default.createElement(AlertDialogContent, null, /* @__PURE__ */ React53__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React53__default.createElement(AlertDialogTitle, null, "Are you sure?"), /* @__PURE__ */ React53__default.createElement(AlertDialogDescription, null, 'This will permanently delete "', itemToDelete == null ? void 0 : itemToDelete.name, '".')), /* @__PURE__ */ React53__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React53__default.createElement(AlertDialogCancel, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(
12640
+ AlertDialogAction,
12641
+ {
12642
+ onClick: confirmDelete,
12643
+ disabled: isPending,
12644
+ className: "bg-destructive hover:bg-destructive/90"
12645
+ },
12646
+ isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
12647
+ " ",
12648
+ "Delete"
12649
+ ))))));
12650
+ }
12651
+ function ContextManager() {
12652
+ const [items, setItems] = useState([]);
12653
+ const [isLoading, setIsLoading] = useState(true);
12654
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
12655
+ const [isAlertOpen, setIsAlertOpen] = useState(false);
12656
+ const [currentItem, setCurrentItem] = useState(null);
12657
+ const [itemName, setItemName] = useState("");
12658
+ const [itemCode, setItemCode] = useState("");
12659
+ const [itemDescription, setItemDescription] = useState("");
12660
+ const [itemToDelete, setItemToDelete] = useState(null);
12661
+ const [isPending, startTransition] = useTransition();
12662
+ const { toast: toast2 } = useToast();
12663
+ useEffect(() => {
12664
+ fetchItems();
12665
+ }, []);
12666
+ const fetchItems = () => {
12667
+ setIsLoading(true);
12668
+ try {
12669
+ setItems(MetadataService.getContexts());
12670
+ } catch (error) {
12671
+ toast2({ title: "Error", description: "Failed to fetch Contexts.", variant: "destructive" });
12672
+ } finally {
12673
+ setIsLoading(false);
12674
+ }
12675
+ };
12676
+ const handleAddItem = () => {
12677
+ setCurrentItem(null);
12678
+ setItemName("");
12679
+ setItemCode("");
12680
+ setItemDescription("");
12681
+ setIsDialogOpen(true);
12682
+ };
12683
+ const handleEditItem = (item) => {
12684
+ setCurrentItem(item);
12685
+ setItemName(item.name);
12686
+ setItemCode(item.code);
12687
+ setItemDescription(item.description || "");
12688
+ setIsDialogOpen(true);
12689
+ };
12690
+ const handleDeleteItem = (item) => {
12691
+ setItemToDelete(item);
12692
+ setIsAlertOpen(true);
12693
+ };
12694
+ const confirmDelete = () => {
12695
+ if (!itemToDelete) return;
12696
+ startTransition(() => {
12697
+ try {
12698
+ MetadataService.deleteContext(itemToDelete.code);
12699
+ toast2({ title: "Success", description: `Context "${itemToDelete.name}" deleted.` });
12700
+ fetchItems();
12701
+ } catch (error) {
12702
+ toast2({ title: "Error", description: "Failed to delete Context.", variant: "destructive" });
12703
+ } finally {
12704
+ setIsAlertOpen(false);
12705
+ setItemToDelete(null);
12706
+ }
12707
+ });
12708
+ };
12709
+ const handleSubmit = () => {
12710
+ if (!itemName.trim() || !itemCode.trim()) {
12711
+ toast2({ title: "Validation Error", description: "Code and Name are required.", variant: "destructive" });
12712
+ return;
12713
+ }
12714
+ startTransition(() => {
12715
+ try {
12716
+ if (currentItem) {
12717
+ MetadataService.updateContext(currentItem.id, itemName, itemCode, itemDescription);
12718
+ } else {
12719
+ MetadataService.addContext(itemName, itemCode, itemDescription);
12720
+ }
12721
+ toast2({ title: "Success", description: "Context saved." });
12722
+ fetchItems();
12723
+ setIsDialogOpen(false);
12724
+ } catch (error) {
12725
+ toast2({ title: "Error", description: "Failed to save Context.", variant: "destructive" });
12726
+ }
12727
+ });
12728
+ };
12729
+ return /* @__PURE__ */ React53__default.createElement(Card, null, /* @__PURE__ */ React53__default.createElement(CardHeader, null, /* @__PURE__ */ React53__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React53__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React53__default.createElement(ScanText, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Contexts"), /* @__PURE__ */ React53__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React53__default.createElement(PlusCircle, { className: "mr-2 h-4 w-4" }), " Add Context"))), /* @__PURE__ */ React53__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React53__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React53__default.createElement(Loader2, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React53__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Contexts found.") : /* @__PURE__ */ React53__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React53__default.createElement(Table2, null, /* @__PURE__ */ React53__default.createElement(TableHeader, null, /* @__PURE__ */ React53__default.createElement(TableRow, null, /* @__PURE__ */ React53__default.createElement(TableHead, null, "Code"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Name"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Description"), /* @__PURE__ */ React53__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React53__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React53__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-mono text-xs" }, item.code), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-medium" }, item.name), /* @__PURE__ */ React53__default.createElement(TableCell, null, item.description), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React53__default.createElement(Edit3, { className: "h-4 w-4" })), /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React53__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React53__default.createElement(Dialog, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React53__default.createElement(DialogContent, { className: "sm:max-w-md" }, /* @__PURE__ */ React53__default.createElement(DialogHeader, null, /* @__PURE__ */ React53__default.createElement(DialogTitle, null, currentItem ? "Edit Context" : "Add New Context")), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemCode" }, "Code"), /* @__PURE__ */ React53__default.createElement(Input, { id: "itemCode", value: itemCode, onChange: (e) => setItemCode(e.target.value.toUpperCase()), placeholder: "e.g., HIST_INQ", disabled: !!currentItem })), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemName" }, "Name"), /* @__PURE__ */ React53__default.createElement(Input, { id: "itemName", value: itemName, onChange: (e) => setItemName(e.target.value), placeholder: "e.g., Historical Inquiry" })), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "itemDescription" }, "Description (Optional)"), /* @__PURE__ */ React53__default.createElement(Textarea, { id: "itemDescription", value: itemDescription, onChange: (e) => setItemDescription(e.target.value), placeholder: "e.g., Analyzing primary and secondary sources." }))), /* @__PURE__ */ React53__default.createElement(DialogFooter, null, /* @__PURE__ */ React53__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !itemCode.trim() || !itemName.trim() }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Save")))), /* @__PURE__ */ React53__default.createElement(AlertDialog, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React53__default.createElement(AlertDialogContent, null, /* @__PURE__ */ React53__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React53__default.createElement(AlertDialogTitle, null, "Are you sure?"), /* @__PURE__ */ React53__default.createElement(AlertDialogDescription, null, 'This will permanently delete "', itemToDelete == null ? void 0 : itemToDelete.name, '".')), /* @__PURE__ */ React53__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React53__default.createElement(AlertDialogCancel, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(AlertDialogAction, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Delete"))))));
12730
+ }
12731
+ var knowledgeDimensions = ["Factual", "Conceptual", "Procedural"];
12732
+ var rawDifficultyLevels = ["E", "E~M", "M", "M~H", "H"];
12733
+ function ApproachManager() {
12734
+ var _a, _b, _c;
12735
+ const [items, setItems] = useState([]);
12736
+ const [bloomLevels, setBloomLevels] = useState([]);
12737
+ const [iSpringQuizTypeOptions, setISpringQuizTypeOptions] = useState([]);
12738
+ const [isLoading, setIsLoading] = useState(true);
12739
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
12740
+ const [isAlertOpen, setIsAlertOpen] = useState(false);
12741
+ const [currentItem, setCurrentItem] = useState(null);
12742
+ const [formState, setFormState] = useState({});
12743
+ const [itemToDelete, setItemToDelete] = useState(null);
12744
+ const [isPending, startTransition] = useTransition();
12745
+ const { toast: toast2 } = useToast();
12746
+ const fetchItems = () => {
12747
+ setIsLoading(true);
12748
+ try {
12749
+ const approaches = MetadataService.getApproaches();
12750
+ const bloomLevelsData = MetadataService.getBloomLevels();
12751
+ const questionTypesData = MetadataService.getQuestionTypes();
12752
+ setItems(approaches);
12753
+ setBloomLevels(bloomLevelsData);
12754
+ setISpringQuizTypeOptions(questionTypesData.map((qt) => ({ label: qt.name, value: qt.code })));
12755
+ } catch (error) {
12756
+ toast2({ title: "Error", description: "Failed to fetch metadata.", variant: "destructive" });
12757
+ } finally {
12758
+ setIsLoading(false);
12759
+ }
12760
+ };
12761
+ useEffect(() => {
12762
+ fetchItems();
12763
+ }, []);
12764
+ const resetForm = () => {
12765
+ setFormState({
12766
+ knowledgeDimension: "Factual",
12767
+ rawDifficulty: "E",
12768
+ bloomLevelCode: bloomLevels.length > 0 ? bloomLevels[0].code : "",
12769
+ iSpringQuizType: iSpringQuizTypeOptions.length > 0 ? iSpringQuizTypeOptions[0].value : "multiple_choice"
12770
+ });
12771
+ };
12772
+ const handleAddItem = () => {
12773
+ setCurrentItem(null);
12774
+ resetForm();
12775
+ setIsDialogOpen(true);
12776
+ };
12777
+ const handleEditItem = (item) => {
12778
+ setCurrentItem(item);
12779
+ setFormState({
12780
+ code: item.code,
12781
+ verbEn: item.verbEn,
12782
+ verbVi: item.verbVi,
12783
+ bloomLevelCode: item.bloomLevelCode,
12784
+ knowledgeDimension: item.knowledgeDimension,
12785
+ iSpringQuizType: item.iSpringQuizType,
12786
+ rawDifficulty: item.rawDifficulty,
12787
+ suggestContext: item.suggestContext,
12788
+ exampleEn: item.exampleEn,
12789
+ exampleVi: item.exampleVi
12790
+ });
12791
+ setIsDialogOpen(true);
12792
+ };
12793
+ const handleDeleteItem = (item) => {
12794
+ setItemToDelete(item);
12795
+ setIsAlertOpen(true);
12796
+ };
12797
+ const confirmDelete = () => {
12798
+ if (!itemToDelete) return;
12799
+ startTransition(() => {
12800
+ try {
12801
+ MetadataService.deleteApproach(itemToDelete.code);
12802
+ toast2({ title: "Success", description: `Approach "${itemToDelete.code}" deleted.` });
12803
+ fetchItems();
12804
+ } catch (error) {
12805
+ toast2({ title: "Error", description: "Failed to delete Approach.", variant: "destructive" });
12806
+ } finally {
12807
+ setIsAlertOpen(false);
12808
+ setItemToDelete(null);
12809
+ }
12810
+ });
12811
+ };
12812
+ const handleSubmit = () => {
12813
+ const { code, verbEn, verbVi } = formState;
12814
+ if (!(code == null ? void 0 : code.trim()) || !(verbEn == null ? void 0 : verbEn.trim()) || !(verbVi == null ? void 0 : verbVi.trim())) {
12815
+ toast2({ title: "Validation Error", description: "Code, Verb (EN), and Verb (VI) are required.", variant: "destructive" });
12816
+ return;
12817
+ }
12818
+ startTransition(() => {
12819
+ try {
12820
+ if (currentItem) {
12821
+ MetadataService.updateApproach(currentItem.id, formState);
12822
+ toast2({ title: "Success", description: "Approach updated." });
12823
+ } else {
12824
+ MetadataService.addApproach(formState);
12825
+ toast2({ title: "Success", description: "Approach added." });
12826
+ }
12827
+ fetchItems();
12828
+ setIsDialogOpen(false);
12829
+ } catch (error) {
12830
+ toast2({ title: "Error", description: error.message || "Failed to save Approach.", variant: "destructive" });
12831
+ }
12832
+ });
12833
+ };
12834
+ return /* @__PURE__ */ React53__default.createElement(Card, null, /* @__PURE__ */ React53__default.createElement(CardHeader, null, /* @__PURE__ */ React53__default.createElement(CardTitle, { className: "flex justify-between items-center" }, /* @__PURE__ */ React53__default.createElement("span", { className: "flex items-center" }, /* @__PURE__ */ React53__default.createElement(Settings2, { className: "mr-2 h-5 w-5 text-primary" }), " Manage Approaches"), /* @__PURE__ */ React53__default.createElement(Button, { onClick: handleAddItem, size: "sm" }, /* @__PURE__ */ React53__default.createElement(PlusCircle, { className: "mr-2 h-4 w-4" }), " Add Approach"))), /* @__PURE__ */ React53__default.createElement(CardContent, null, isLoading ? /* @__PURE__ */ React53__default.createElement("div", { className: "flex justify-center items-center h-32" }, /* @__PURE__ */ React53__default.createElement(Loader2, { className: "h-8 w-8 animate-spin text-primary" })) : items.length === 0 ? /* @__PURE__ */ React53__default.createElement("p", { className: "text-center text-muted-foreground py-4" }, "No Approaches found.") : /* @__PURE__ */ React53__default.createElement("div", { className: "overflow-x-auto" }, /* @__PURE__ */ React53__default.createElement(Table2, null, /* @__PURE__ */ React53__default.createElement(TableHeader, null, /* @__PURE__ */ React53__default.createElement(TableRow, null, /* @__PURE__ */ React53__default.createElement(TableHead, null, "Approach ID"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Verb (VI)"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "Cognitive Level"), /* @__PURE__ */ React53__default.createElement(TableHead, null, "iSpring Type"), /* @__PURE__ */ React53__default.createElement(TableHead, { className: "text-right w-[120px]" }, "Actions"))), /* @__PURE__ */ React53__default.createElement(TableBody, null, items.map((item) => /* @__PURE__ */ React53__default.createElement(TableRow, { key: item.id }, /* @__PURE__ */ React53__default.createElement(TableCell, { className: "font-medium" }, item.code), /* @__PURE__ */ React53__default.createElement(TableCell, null, item.verbVi), /* @__PURE__ */ React53__default.createElement(TableCell, null, item.bloomLevelCode), /* @__PURE__ */ React53__default.createElement(TableCell, null, item.iSpringQuizType), /* @__PURE__ */ React53__default.createElement(TableCell, { className: "text-right" }, /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleEditItem(item), className: "mr-2" }, /* @__PURE__ */ React53__default.createElement(Edit3, { className: "h-4 w-4" })), /* @__PURE__ */ React53__default.createElement(Button, { variant: "ghost", size: "icon", onClick: () => handleDeleteItem(item), className: "text-destructive hover:text-destructive" }, /* @__PURE__ */ React53__default.createElement(Trash2, { className: "h-4 w-4" })))))))), /* @__PURE__ */ React53__default.createElement(Dialog, { open: isDialogOpen, onOpenChange: setIsDialogOpen }, /* @__PURE__ */ React53__default.createElement(DialogContent, { className: "sm:max-w-2xl max-h-[90vh] overflow-y-auto" }, /* @__PURE__ */ React53__default.createElement(DialogHeader, null, /* @__PURE__ */ React53__default.createElement(DialogTitle, null, currentItem ? "Edit Approach" : "Add New Approach")), /* @__PURE__ */ React53__default.createElement("div", { className: "grid gap-4 py-4" }, /* @__PURE__ */ React53__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "code" }, "Approach Code"), /* @__PURE__ */ React53__default.createElement(Input, { id: "code", value: formState.code || "", onChange: (e) => setFormState((s) => __spreadProps(__spreadValues({}, s), { code: e.target.value.toUpperCase() })), placeholder: "e.g., REM-FAC-IDT-MCQ", disabled: !!currentItem })), /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "verbEn" }, "Verb (English)"), /* @__PURE__ */ React53__default.createElement(Input, { id: "verbEn", value: formState.verbEn || "", onChange: (e) => setFormState((s) => __spreadProps(__spreadValues({}, s), { verbEn: e.target.value })), placeholder: "e.g., Identify" }))), /* @__PURE__ */ React53__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" }, /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "verbVi" }, "Verb (Vietnamese)"), /* @__PURE__ */ React53__default.createElement(Input, { id: "verbVi", value: formState.verbVi || "", onChange: (e) => setFormState((s) => __spreadProps(__spreadValues({}, s), { verbVi: e.target.value })), placeholder: "e.g., Nh\u1EADn d\u1EA1ng" })), /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "bloomLevelCode" }, "Cognitive Level"), /* @__PURE__ */ React53__default.createElement(Select, { value: formState.bloomLevelCode, onValueChange: (v) => setFormState((s) => __spreadProps(__spreadValues({}, s), { bloomLevelCode: v })) }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, null, /* @__PURE__ */ React53__default.createElement(SelectValue, null)), /* @__PURE__ */ React53__default.createElement(SelectContent, null, bloomLevels.map((level) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: level.code, value: level.code }, level.name)))))), /* @__PURE__ */ React53__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4" }, /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "knowledgeDimension" }, "Knowledge Dimension"), /* @__PURE__ */ React53__default.createElement(Select, { value: formState.knowledgeDimension, onValueChange: (v) => setFormState((s) => __spreadProps(__spreadValues({}, s), { knowledgeDimension: v })) }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, null, /* @__PURE__ */ React53__default.createElement(SelectValue, null)), /* @__PURE__ */ React53__default.createElement(SelectContent, null, knowledgeDimensions.map((kd) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: kd, value: kd }, kd))))), /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "iSpringQuizType" }, "iSpring Quiz Type"), /* @__PURE__ */ React53__default.createElement(Select, { value: formState.iSpringQuizType, onValueChange: (v) => setFormState((s) => __spreadProps(__spreadValues({}, s), { iSpringQuizType: v })) }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, null, /* @__PURE__ */ React53__default.createElement(SelectValue, null)), /* @__PURE__ */ React53__default.createElement(SelectContent, null, iSpringQuizTypeOptions.map((qt) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: qt.value, value: qt.value }, qt.label))))), /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "rawDifficulty" }, "Raw Difficulty"), /* @__PURE__ */ React53__default.createElement(Select, { value: formState.rawDifficulty, onValueChange: (v) => setFormState((s) => __spreadProps(__spreadValues({}, s), { rawDifficulty: v })) }, /* @__PURE__ */ React53__default.createElement(SelectTrigger, null, /* @__PURE__ */ React53__default.createElement(SelectValue, null)), /* @__PURE__ */ React53__default.createElement(SelectContent, null, rawDifficultyLevels.map((rd) => /* @__PURE__ */ React53__default.createElement(SelectItem, { key: rd, value: rd }, rd)))))), /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "suggestContext" }, "Suggest Context (comma-separated codes)"), /* @__PURE__ */ React53__default.createElement(Input, { id: "suggestContext", value: formState.suggestContext || "", onChange: (e) => setFormState((s) => __spreadProps(__spreadValues({}, s), { suggestContext: e.target.value })), placeholder: "e.g., A, B, D, G, H" })), /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "exampleEn" }, "Example (English)"), /* @__PURE__ */ React53__default.createElement(Textarea, { id: "exampleEn", value: formState.exampleEn || "", onChange: (e) => setFormState((s) => __spreadProps(__spreadValues({}, s), { exampleEn: e.target.value })), placeholder: "English example prompt..." })), /* @__PURE__ */ React53__default.createElement("div", null, /* @__PURE__ */ React53__default.createElement(Label, { htmlFor: "exampleVi" }, "Example (Vietnamese)"), /* @__PURE__ */ React53__default.createElement(Textarea, { id: "exampleVi", value: formState.exampleVi || "", onChange: (e) => setFormState((s) => __spreadProps(__spreadValues({}, s), { exampleVi: e.target.value })), placeholder: "Vietnamese example prompt..." }))), /* @__PURE__ */ React53__default.createElement(DialogFooter, null, /* @__PURE__ */ React53__default.createElement(Button, { type: "button", variant: "outline", onClick: () => setIsDialogOpen(false), disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(Button, { type: "submit", onClick: handleSubmit, disabled: isPending || !((_a = formState.code) == null ? void 0 : _a.trim()) || !((_b = formState.verbEn) == null ? void 0 : _b.trim()) || !((_c = formState.verbVi) == null ? void 0 : _c.trim()) }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), " Save")))), /* @__PURE__ */ React53__default.createElement(AlertDialog, { open: isAlertOpen, onOpenChange: setIsAlertOpen }, /* @__PURE__ */ React53__default.createElement(AlertDialogContent, null, /* @__PURE__ */ React53__default.createElement(AlertDialogHeader, null, /* @__PURE__ */ React53__default.createElement(AlertDialogTitle, null, "Are you sure?"), /* @__PURE__ */ React53__default.createElement(AlertDialogDescription, null, 'This will permanently delete "', itemToDelete == null ? void 0 : itemToDelete.code, '".')), /* @__PURE__ */ React53__default.createElement(AlertDialogFooter, null, /* @__PURE__ */ React53__default.createElement(AlertDialogCancel, { disabled: isPending }, "Cancel"), /* @__PURE__ */ React53__default.createElement(AlertDialogAction, { onClick: confirmDelete, disabled: isPending, className: "bg-destructive hover:bg-destructive/90" }, isPending && /* @__PURE__ */ React53__default.createElement(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), " Delete"))))));
12835
+ }
12836
+
12837
+ // src/react-ui/components/metadata/MetadataTabs.tsx
12838
+ function MetadataTabs() {
12839
+ const tabGridCols = "grid-cols-3 sm:grid-cols-3 md:grid-cols-5 lg:grid-cols-9";
12840
+ return /* @__PURE__ */ React53__default.createElement(Tabs, { defaultValue: "subjects", className: "w-full" }, /* @__PURE__ */ React53__default.createElement(TabsList, { className: `grid w-full ${tabGridCols}` }, /* @__PURE__ */ React53__default.createElement(TabsTrigger, { value: "subjects" }, "Subjects"), /* @__PURE__ */ React53__default.createElement(TabsTrigger, { value: "gradeLevels" }, "Grade Levels"), /* @__PURE__ */ React53__default.createElement(TabsTrigger, { value: "topics" }, "Topics"), /* @__PURE__ */ React53__default.createElement(TabsTrigger, { value: "categories" }, "Categories"), /* @__PURE__ */ React53__default.createElement(TabsTrigger, { value: "bloomLevels" }, "Bloom Levels"), /* @__PURE__ */ React53__default.createElement(TabsTrigger, { value: "questionTypes" }, "Question Types"), /* @__PURE__ */ React53__default.createElement(TabsTrigger, { value: "learningObjectives" }, "Learning Objectives"), /* @__PURE__ */ React53__default.createElement(TabsTrigger, { value: "contexts" }, "Contexts"), /* @__PURE__ */ React53__default.createElement(TabsTrigger, { value: "approaches" }, "Approaches")), /* @__PURE__ */ React53__default.createElement(TabsContent, { value: "subjects" }, /* @__PURE__ */ React53__default.createElement(Suspense, { fallback: /* @__PURE__ */ React53__default.createElement(Skeleton, { className: "h-64 w-full" }) }, /* @__PURE__ */ React53__default.createElement(SubjectManager, null))), /* @__PURE__ */ React53__default.createElement(TabsContent, { value: "gradeLevels" }, /* @__PURE__ */ React53__default.createElement(Suspense, { fallback: /* @__PURE__ */ React53__default.createElement(Skeleton, { className: "h-64 w-full" }) }, /* @__PURE__ */ React53__default.createElement(GradeLevelManager, null))), /* @__PURE__ */ React53__default.createElement(TabsContent, { value: "topics" }, /* @__PURE__ */ React53__default.createElement(Suspense, { fallback: /* @__PURE__ */ React53__default.createElement(Skeleton, { className: "h-64 w-full" }) }, /* @__PURE__ */ React53__default.createElement(TopicManager, null))), /* @__PURE__ */ React53__default.createElement(TabsContent, { value: "categories" }, /* @__PURE__ */ React53__default.createElement(Suspense, { fallback: /* @__PURE__ */ React53__default.createElement(Skeleton, { className: "h-64 w-full" }) }, /* @__PURE__ */ React53__default.createElement(CategoryManager, null))), /* @__PURE__ */ React53__default.createElement(TabsContent, { value: "bloomLevels" }, /* @__PURE__ */ React53__default.createElement(Suspense, { fallback: /* @__PURE__ */ React53__default.createElement(Skeleton, { className: "h-64 w-full" }) }, /* @__PURE__ */ React53__default.createElement(BloomLevelManager, null))), /* @__PURE__ */ React53__default.createElement(TabsContent, { value: "questionTypes" }, /* @__PURE__ */ React53__default.createElement(Suspense, { fallback: /* @__PURE__ */ React53__default.createElement(Skeleton, { className: "h-64 w-full" }) }, /* @__PURE__ */ React53__default.createElement(QuestionTypeManager, null))), /* @__PURE__ */ React53__default.createElement(TabsContent, { value: "learningObjectives" }, /* @__PURE__ */ React53__default.createElement(Suspense, { fallback: /* @__PURE__ */ React53__default.createElement(Skeleton, { className: "h-64 w-full" }) }, /* @__PURE__ */ React53__default.createElement(LearningObjectiveManager, null))), /* @__PURE__ */ React53__default.createElement(TabsContent, { value: "contexts" }, /* @__PURE__ */ React53__default.createElement(Suspense, { fallback: /* @__PURE__ */ React53__default.createElement(Skeleton, { className: "h-64 w-full" }) }, /* @__PURE__ */ React53__default.createElement(ContextManager, null))), /* @__PURE__ */ React53__default.createElement(TabsContent, { value: "approaches" }, /* @__PURE__ */ React53__default.createElement(Suspense, { fallback: /* @__PURE__ */ React53__default.createElement(Skeleton, { className: "h-64 w-full" }) }, /* @__PURE__ */ React53__default.createElement(ApproachManager, null))));
12841
+ }
11094
12842
  var ToastProvider = ToastPrimitives.Provider;
11095
12843
  var ToastViewport = React53.forwardRef((_a, ref) => {
11096
12844
  var _b = _a, { className } = _b, props = __objRest(_b, ["className"]);
@@ -11193,4 +12941,4 @@ function Toaster() {
11193
12941
  }), /* @__PURE__ */ React.createElement(ToastViewport, null));
11194
12942
  }
11195
12943
 
11196
- export { AIFullQuizGeneratorModal, AIQuestionGeneratorModal, APIKeyService, AchievementService, EditQuestionModal, GEMINI_API_KEY_SERVICE_NAME, ImportQuestionsModal, KnowledgeCardService, PracticeHistoryService, QuestionImportService, QuizAuthoringTool, QuizEditorService, QuizEngine, QuoteService, SCORMExportModal, SCORMService, Toaster, UserConfigService, assessAndMapDocument, cn, emptyQuiz, exportQuizAsSCORMZip, generateFillInTheBlanksQuestion, generateLauncherHTML, generateLearningAnalysis, generateMCQQuestion, generateMRQQuestion, generateMatchingQuestion, generateMotivationalQuote, generateNumericQuestion, generatePracticeSuggestion, generateQuestionsFromQuizPlan, generateQuizFromText, generateQuizPlan, generateQuizReview, generateSCORMManifest, generateSequenceQuestion, generateShortAnswerQuestion, generateSingleKnowledgeCard, generateTrueFalseQuestion, generateUniqueId, planKnowledgeCards, sampleQuiz, toast, useToast };
12944
+ export { AIFullQuizGeneratorModal, AIQuestionGeneratorModal, APIKeyManagerModal, APIKeyService, AchievementService, ApproachManager, BloomLevelManager, CategoryManager, ContextManager, EditQuestionModal, GEMINI_API_KEY_SERVICE_NAME, GradeLevelManager, ImportQuestionsModal, KnowledgeCardService, LearningObjectiveManager, MetadataService, MetadataTabs, PracticeHistoryService, QuestionBankService, QuestionFilters, QuestionFormDialog, QuestionImportService, QuestionList, QuestionTypeManager, QuizAuthoringTool, QuizEditorService, QuizEngine, QuoteService, SCORMExportModal, SCORMService, SubjectManager, Toaster, TopicManager, UserConfigService, assessAndMapDocument, cn, emptyQuiz, exportQuizAsSCORMZip, generateFillInTheBlanksQuestion, generateLauncherHTML, generateLearningAnalysis, generateMCQQuestion, generateMRQQuestion, generateMatchingQuestion, generateMotivationalQuote, generateNumericQuestion, generatePracticeSuggestion, generateQuestionsFromQuizPlan, generateQuizFromText, generateQuizPlan, generateQuizReview, generateSCORMManifest, generateSequenceQuestion, generateShortAnswerQuestion, generateSingleKnowledgeCard, generateTrueFalseQuestion, generateUniqueId, planKnowledgeCards, sampleQuiz, toast, useToast };