@thanh01.pmt/interactive-quiz-kit 1.0.68 → 1.0.70
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/ai.cjs +42 -44
- package/dist/ai.mjs +42 -44
- package/dist/authoring.cjs +52 -54
- package/dist/authoring.mjs +52 -54
- package/dist/react-ui.cjs +52 -54
- package/dist/react-ui.mjs +52 -54
- package/package.json +1 -1
package/dist/ai.cjs
CHANGED
|
@@ -2496,6 +2496,7 @@ function validateConsecutiveTypes(quizPlan) {
|
|
|
2496
2496
|
|
|
2497
2497
|
// src/services/TopicDataService.ts
|
|
2498
2498
|
var TopicDataService = class {
|
|
2499
|
+
// ... saveData, mergeData, getData, clearData methods remain the same ...
|
|
2499
2500
|
static saveData(data) {
|
|
2500
2501
|
try {
|
|
2501
2502
|
if (typeof window === "undefined") return;
|
|
@@ -2540,47 +2541,41 @@ var TopicDataService = class {
|
|
|
2540
2541
|
}
|
|
2541
2542
|
const headerLine = lines.shift();
|
|
2542
2543
|
const headers = headerLine.split(" ").map((h) => h.trim());
|
|
2543
|
-
|
|
2544
|
-
const errorMsg = `Invalid TSV header. Expected: "${this.EXPECTED_HEADERS.join(" ")}". Received: "${headers.join(" ")}"`;
|
|
2545
|
-
return { data: [], errors: [errorMsg] };
|
|
2546
|
-
}
|
|
2544
|
+
const headerMap = headers.map((h) => this.HEADER_MAP[h] || null);
|
|
2547
2545
|
const data = [];
|
|
2548
2546
|
const errors = [];
|
|
2549
2547
|
lines.forEach((line, index) => {
|
|
2550
|
-
const values = line.split(" ")
|
|
2551
|
-
|
|
2552
|
-
|
|
2548
|
+
const values = line.split(" ");
|
|
2549
|
+
const rowObject = {};
|
|
2550
|
+
headerMap.forEach((propName, i) => {
|
|
2551
|
+
if (propName) {
|
|
2552
|
+
const value = values[i]?.trim() || "";
|
|
2553
|
+
if (propName === "keywords" || propName === "stemElements" || propName === "bloomLevelsGuideline") {
|
|
2554
|
+
rowObject[propName] = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2555
|
+
} else {
|
|
2556
|
+
rowObject[propName] = value;
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
});
|
|
2560
|
+
if (!rowObject.code) {
|
|
2561
|
+
errors.push(`Line ${index + 2}: Missing required value for 'LO ID'.`);
|
|
2553
2562
|
return;
|
|
2554
2563
|
}
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
name,
|
|
2558
|
-
loDescription,
|
|
2559
|
-
subject,
|
|
2560
|
-
category,
|
|
2561
|
-
topic,
|
|
2562
|
-
keywordsStr,
|
|
2563
|
-
grade,
|
|
2564
|
-
stemElementsStr,
|
|
2565
|
-
bloomLevelsStr
|
|
2566
|
-
] = values;
|
|
2567
|
-
if (!loId || !loDescription || !subject || !category || !topic) {
|
|
2568
|
-
errors.push(`Line ${index + 2}: Missing required fields (LO ID, LO Description, Subject, Category, or Topic).`);
|
|
2569
|
-
return;
|
|
2564
|
+
if (!rowObject.name) {
|
|
2565
|
+
rowObject.name = rowObject.code;
|
|
2570
2566
|
}
|
|
2571
2567
|
const learningObjective = {
|
|
2572
2568
|
id: generateUniqueId("lo_"),
|
|
2573
|
-
code:
|
|
2574
|
-
name,
|
|
2575
|
-
description:
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
bloomLevelsGuideline: bloomLevelsStr.split(",").map((b) => b.trim()).filter(Boolean)
|
|
2569
|
+
code: rowObject.code,
|
|
2570
|
+
name: rowObject.name,
|
|
2571
|
+
description: rowObject.description,
|
|
2572
|
+
subject: rowObject.subject || "",
|
|
2573
|
+
category: rowObject.category || "",
|
|
2574
|
+
topic: rowObject.topic || "",
|
|
2575
|
+
grade: rowObject.grade || "",
|
|
2576
|
+
keywords: rowObject.keywords || [],
|
|
2577
|
+
stemElements: rowObject.stemElements || [],
|
|
2578
|
+
bloomLevelsGuideline: rowObject.bloomLevelsGuideline || []
|
|
2584
2579
|
};
|
|
2585
2580
|
data.push(learningObjective);
|
|
2586
2581
|
});
|
|
@@ -2608,17 +2603,20 @@ var TopicDataService = class {
|
|
|
2608
2603
|
}
|
|
2609
2604
|
};
|
|
2610
2605
|
TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
"LO
|
|
2614
|
-
"
|
|
2615
|
-
|
|
2616
|
-
"
|
|
2617
|
-
"
|
|
2618
|
-
"
|
|
2619
|
-
"
|
|
2620
|
-
"
|
|
2621
|
-
|
|
2606
|
+
// Define a map for flexible header mapping
|
|
2607
|
+
TopicDataService.HEADER_MAP = {
|
|
2608
|
+
"LO ID": "code",
|
|
2609
|
+
"LO Name": "name",
|
|
2610
|
+
// Ready for future addition
|
|
2611
|
+
"LO Description": "description",
|
|
2612
|
+
"Subject": "subject",
|
|
2613
|
+
"Category": "category",
|
|
2614
|
+
"Topic": "topic",
|
|
2615
|
+
"Keywords": "keywords",
|
|
2616
|
+
"Grade": "grade",
|
|
2617
|
+
"STEM Element(s)": "stemElements",
|
|
2618
|
+
"Bloom\u2019s Level(s) Guideline": "bloomLevelsGuideline"
|
|
2619
|
+
};
|
|
2622
2620
|
|
|
2623
2621
|
// src/ai/flows/generate-questions-from-quiz-plan.ts
|
|
2624
2622
|
var MAX_ATTEMPTS = 3;
|
package/dist/ai.mjs
CHANGED
|
@@ -2494,6 +2494,7 @@ function validateConsecutiveTypes(quizPlan) {
|
|
|
2494
2494
|
|
|
2495
2495
|
// src/services/TopicDataService.ts
|
|
2496
2496
|
var TopicDataService = class {
|
|
2497
|
+
// ... saveData, mergeData, getData, clearData methods remain the same ...
|
|
2497
2498
|
static saveData(data) {
|
|
2498
2499
|
try {
|
|
2499
2500
|
if (typeof window === "undefined") return;
|
|
@@ -2538,47 +2539,41 @@ var TopicDataService = class {
|
|
|
2538
2539
|
}
|
|
2539
2540
|
const headerLine = lines.shift();
|
|
2540
2541
|
const headers = headerLine.split(" ").map((h) => h.trim());
|
|
2541
|
-
|
|
2542
|
-
const errorMsg = `Invalid TSV header. Expected: "${this.EXPECTED_HEADERS.join(" ")}". Received: "${headers.join(" ")}"`;
|
|
2543
|
-
return { data: [], errors: [errorMsg] };
|
|
2544
|
-
}
|
|
2542
|
+
const headerMap = headers.map((h) => this.HEADER_MAP[h] || null);
|
|
2545
2543
|
const data = [];
|
|
2546
2544
|
const errors = [];
|
|
2547
2545
|
lines.forEach((line, index) => {
|
|
2548
|
-
const values = line.split(" ")
|
|
2549
|
-
|
|
2550
|
-
|
|
2546
|
+
const values = line.split(" ");
|
|
2547
|
+
const rowObject = {};
|
|
2548
|
+
headerMap.forEach((propName, i) => {
|
|
2549
|
+
if (propName) {
|
|
2550
|
+
const value = values[i]?.trim() || "";
|
|
2551
|
+
if (propName === "keywords" || propName === "stemElements" || propName === "bloomLevelsGuideline") {
|
|
2552
|
+
rowObject[propName] = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2553
|
+
} else {
|
|
2554
|
+
rowObject[propName] = value;
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
});
|
|
2558
|
+
if (!rowObject.code) {
|
|
2559
|
+
errors.push(`Line ${index + 2}: Missing required value for 'LO ID'.`);
|
|
2551
2560
|
return;
|
|
2552
2561
|
}
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
name,
|
|
2556
|
-
loDescription,
|
|
2557
|
-
subject,
|
|
2558
|
-
category,
|
|
2559
|
-
topic,
|
|
2560
|
-
keywordsStr,
|
|
2561
|
-
grade,
|
|
2562
|
-
stemElementsStr,
|
|
2563
|
-
bloomLevelsStr
|
|
2564
|
-
] = values;
|
|
2565
|
-
if (!loId || !loDescription || !subject || !category || !topic) {
|
|
2566
|
-
errors.push(`Line ${index + 2}: Missing required fields (LO ID, LO Description, Subject, Category, or Topic).`);
|
|
2567
|
-
return;
|
|
2562
|
+
if (!rowObject.name) {
|
|
2563
|
+
rowObject.name = rowObject.code;
|
|
2568
2564
|
}
|
|
2569
2565
|
const learningObjective = {
|
|
2570
2566
|
id: generateUniqueId("lo_"),
|
|
2571
|
-
code:
|
|
2572
|
-
name,
|
|
2573
|
-
description:
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
bloomLevelsGuideline: bloomLevelsStr.split(",").map((b) => b.trim()).filter(Boolean)
|
|
2567
|
+
code: rowObject.code,
|
|
2568
|
+
name: rowObject.name,
|
|
2569
|
+
description: rowObject.description,
|
|
2570
|
+
subject: rowObject.subject || "",
|
|
2571
|
+
category: rowObject.category || "",
|
|
2572
|
+
topic: rowObject.topic || "",
|
|
2573
|
+
grade: rowObject.grade || "",
|
|
2574
|
+
keywords: rowObject.keywords || [],
|
|
2575
|
+
stemElements: rowObject.stemElements || [],
|
|
2576
|
+
bloomLevelsGuideline: rowObject.bloomLevelsGuideline || []
|
|
2582
2577
|
};
|
|
2583
2578
|
data.push(learningObjective);
|
|
2584
2579
|
});
|
|
@@ -2606,17 +2601,20 @@ var TopicDataService = class {
|
|
|
2606
2601
|
}
|
|
2607
2602
|
};
|
|
2608
2603
|
TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
"LO
|
|
2612
|
-
"
|
|
2613
|
-
|
|
2614
|
-
"
|
|
2615
|
-
"
|
|
2616
|
-
"
|
|
2617
|
-
"
|
|
2618
|
-
"
|
|
2619
|
-
|
|
2604
|
+
// Define a map for flexible header mapping
|
|
2605
|
+
TopicDataService.HEADER_MAP = {
|
|
2606
|
+
"LO ID": "code",
|
|
2607
|
+
"LO Name": "name",
|
|
2608
|
+
// Ready for future addition
|
|
2609
|
+
"LO Description": "description",
|
|
2610
|
+
"Subject": "subject",
|
|
2611
|
+
"Category": "category",
|
|
2612
|
+
"Topic": "topic",
|
|
2613
|
+
"Keywords": "keywords",
|
|
2614
|
+
"Grade": "grade",
|
|
2615
|
+
"STEM Element(s)": "stemElements",
|
|
2616
|
+
"Bloom\u2019s Level(s) Guideline": "bloomLevelsGuideline"
|
|
2617
|
+
};
|
|
2620
2618
|
|
|
2621
2619
|
// src/ai/flows/generate-questions-from-quiz-plan.ts
|
|
2622
2620
|
var MAX_ATTEMPTS = 3;
|
package/dist/authoring.cjs
CHANGED
|
@@ -8832,6 +8832,7 @@ init_react_shim();
|
|
|
8832
8832
|
// src/services/TopicDataService.ts
|
|
8833
8833
|
init_react_shim();
|
|
8834
8834
|
var TopicDataService = class {
|
|
8835
|
+
// ... saveData, mergeData, getData, clearData methods remain the same ...
|
|
8835
8836
|
static saveData(data) {
|
|
8836
8837
|
try {
|
|
8837
8838
|
if (typeof window === "undefined") return;
|
|
@@ -8876,47 +8877,41 @@ var TopicDataService = class {
|
|
|
8876
8877
|
}
|
|
8877
8878
|
const headerLine = lines.shift();
|
|
8878
8879
|
const headers = headerLine.split(" ").map((h2) => h2.trim());
|
|
8879
|
-
|
|
8880
|
-
const errorMsg = `Invalid TSV header. Expected: "${this.EXPECTED_HEADERS.join(" ")}". Received: "${headers.join(" ")}"`;
|
|
8881
|
-
return { data: [], errors: [errorMsg] };
|
|
8882
|
-
}
|
|
8880
|
+
const headerMap = headers.map((h2) => this.HEADER_MAP[h2] || null);
|
|
8883
8881
|
const data = [];
|
|
8884
8882
|
const errors2 = [];
|
|
8885
8883
|
lines.forEach((line, index3) => {
|
|
8886
|
-
const values = line.split(" ")
|
|
8887
|
-
|
|
8888
|
-
|
|
8884
|
+
const values = line.split(" ");
|
|
8885
|
+
const rowObject = {};
|
|
8886
|
+
headerMap.forEach((propName, i) => {
|
|
8887
|
+
if (propName) {
|
|
8888
|
+
const value = values[i]?.trim() || "";
|
|
8889
|
+
if (propName === "keywords" || propName === "stemElements" || propName === "bloomLevelsGuideline") {
|
|
8890
|
+
rowObject[propName] = value.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
8891
|
+
} else {
|
|
8892
|
+
rowObject[propName] = value;
|
|
8893
|
+
}
|
|
8894
|
+
}
|
|
8895
|
+
});
|
|
8896
|
+
if (!rowObject.code) {
|
|
8897
|
+
errors2.push(`Line ${index3 + 2}: Missing required value for 'LO ID'.`);
|
|
8889
8898
|
return;
|
|
8890
8899
|
}
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
name3,
|
|
8894
|
-
loDescription,
|
|
8895
|
-
subject,
|
|
8896
|
-
category,
|
|
8897
|
-
topic,
|
|
8898
|
-
keywordsStr,
|
|
8899
|
-
grade,
|
|
8900
|
-
stemElementsStr,
|
|
8901
|
-
bloomLevelsStr
|
|
8902
|
-
] = values;
|
|
8903
|
-
if (!loId || !loDescription || !subject || !category || !topic) {
|
|
8904
|
-
errors2.push(`Line ${index3 + 2}: Missing required fields (LO ID, LO Description, Subject, Category, or Topic).`);
|
|
8905
|
-
return;
|
|
8900
|
+
if (!rowObject.name) {
|
|
8901
|
+
rowObject.name = rowObject.code;
|
|
8906
8902
|
}
|
|
8907
8903
|
const learningObjective = {
|
|
8908
8904
|
id: generateUniqueId("lo_"),
|
|
8909
|
-
code:
|
|
8910
|
-
name:
|
|
8911
|
-
description:
|
|
8912
|
-
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
bloomLevelsGuideline: bloomLevelsStr.split(",").map((b) => b.trim()).filter(Boolean)
|
|
8905
|
+
code: rowObject.code,
|
|
8906
|
+
name: rowObject.name,
|
|
8907
|
+
description: rowObject.description,
|
|
8908
|
+
subject: rowObject.subject || "",
|
|
8909
|
+
category: rowObject.category || "",
|
|
8910
|
+
topic: rowObject.topic || "",
|
|
8911
|
+
grade: rowObject.grade || "",
|
|
8912
|
+
keywords: rowObject.keywords || [],
|
|
8913
|
+
stemElements: rowObject.stemElements || [],
|
|
8914
|
+
bloomLevelsGuideline: rowObject.bloomLevelsGuideline || []
|
|
8920
8915
|
};
|
|
8921
8916
|
data.push(learningObjective);
|
|
8922
8917
|
});
|
|
@@ -8944,17 +8939,20 @@ var TopicDataService = class {
|
|
|
8944
8939
|
}
|
|
8945
8940
|
};
|
|
8946
8941
|
TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
"LO
|
|
8950
|
-
"
|
|
8951
|
-
|
|
8952
|
-
"
|
|
8953
|
-
"
|
|
8954
|
-
"
|
|
8955
|
-
"
|
|
8956
|
-
"
|
|
8957
|
-
|
|
8942
|
+
// Define a map for flexible header mapping
|
|
8943
|
+
TopicDataService.HEADER_MAP = {
|
|
8944
|
+
"LO ID": "code",
|
|
8945
|
+
"LO Name": "name",
|
|
8946
|
+
// Ready for future addition
|
|
8947
|
+
"LO Description": "description",
|
|
8948
|
+
"Subject": "subject",
|
|
8949
|
+
"Category": "category",
|
|
8950
|
+
"Topic": "topic",
|
|
8951
|
+
"Keywords": "keywords",
|
|
8952
|
+
"Grade": "grade",
|
|
8953
|
+
"STEM Element(s)": "stemElements",
|
|
8954
|
+
"Bloom\u2019s Level(s) Guideline": "bloomLevelsGuideline"
|
|
8955
|
+
};
|
|
8958
8956
|
|
|
8959
8957
|
// src/ai/flows/generate-questions-from-quiz-plan.ts
|
|
8960
8958
|
var MAX_ATTEMPTS = 3;
|
|
@@ -76443,8 +76441,8 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
76443
76441
|
const handleFileSelected = (event) => {
|
|
76444
76442
|
const file = event.target.files?.[0];
|
|
76445
76443
|
if (!file) return;
|
|
76446
|
-
if (file.type
|
|
76447
|
-
toast2({ title: "Invalid File Type", description: "Please select a JSON or
|
|
76444
|
+
if (!file.type.includes("json") && !file.type.includes("tab-separated-values") && !file.name.endsWith(".tsv") && !file.name.endsWith(".txt")) {
|
|
76445
|
+
toast2({ title: "Invalid File Type", description: "Please select a JSON or TSV/TXT file.", variant: "destructive" });
|
|
76448
76446
|
if (event.target) event.target.value = "";
|
|
76449
76447
|
return;
|
|
76450
76448
|
}
|
|
@@ -76452,18 +76450,18 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
76452
76450
|
try {
|
|
76453
76451
|
const fileContent = await file.text();
|
|
76454
76452
|
let records = [];
|
|
76455
|
-
if (file.type
|
|
76453
|
+
if (file.type.includes("json")) {
|
|
76456
76454
|
records = JSON.parse(fileContent);
|
|
76457
76455
|
if (!Array.isArray(records)) throw new Error("JSON file must contain an array of objects.");
|
|
76458
|
-
} else
|
|
76459
|
-
const lines = fileContent.split(/\r
|
|
76460
|
-
if (lines.length < 2) throw new Error("
|
|
76461
|
-
const headers = lines[0].split("
|
|
76456
|
+
} else {
|
|
76457
|
+
const lines = fileContent.split(/\r?\n/).filter((line) => line.trim() !== "");
|
|
76458
|
+
if (lines.length < 2) throw new Error("TSV file must have a header and at least one data row.");
|
|
76459
|
+
const headers = lines[0].split(" ").map((h2) => h2.trim());
|
|
76462
76460
|
records = lines.slice(1).map((line) => {
|
|
76463
|
-
const values = line.split("
|
|
76461
|
+
const values = line.split(" ");
|
|
76464
76462
|
const record = {};
|
|
76465
76463
|
headers.forEach((header, index3) => {
|
|
76466
|
-
record[header] = values[index3];
|
|
76464
|
+
record[header] = values[index3]?.trim() || "";
|
|
76467
76465
|
});
|
|
76468
76466
|
return record;
|
|
76469
76467
|
});
|
|
@@ -76490,7 +76488,7 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
76490
76488
|
}
|
|
76491
76489
|
});
|
|
76492
76490
|
};
|
|
76493
|
-
return /* @__PURE__ */ React119__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React119__namespace.default.createElement(DialogTrigger2, { asChild: true }, /* @__PURE__ */ React119__namespace.default.createElement(Button, { size: "sm", variant: "outline" }, /* @__PURE__ */ React119__namespace.default.createElement(Upload, { className: "mr-2 h-4 w-4" }), " Import ", metadataName)), /* @__PURE__ */ React119__namespace.default.createElement(DialogContent2, null, /* @__PURE__ */ React119__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React119__namespace.default.createElement(DialogTitle2, null, "Bulk Import ", metadataName), /* @__PURE__ */ React119__namespace.default.createElement(DialogDescription2, null, "Import multiple records from a file or by pasting JSON data.")), /* @__PURE__ */ React119__namespace.default.createElement(Tabs2, { defaultValue: "file" }, /* @__PURE__ */ React119__namespace.default.createElement(TabsList2, { className: "grid w-full grid-cols-2" }, /* @__PURE__ */ React119__namespace.default.createElement(TabsTrigger2, { value: "file" }, /* @__PURE__ */ React119__namespace.default.createElement(FileJson, { className: "mr-2 h-4 w-4" }), " From File"), /* @__PURE__ */ React119__namespace.default.createElement(TabsTrigger2, { value: "text" }, /* @__PURE__ */ React119__namespace.default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), " From Text")), /* @__PURE__ */ React119__namespace.default.createElement(TabsContent2, { value: "file", className: "pt-4" }, /* @__PURE__ */ React119__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React119__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, "Select a JSON or
|
|
76491
|
+
return /* @__PURE__ */ React119__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React119__namespace.default.createElement(DialogTrigger2, { asChild: true }, /* @__PURE__ */ React119__namespace.default.createElement(Button, { size: "sm", variant: "outline" }, /* @__PURE__ */ React119__namespace.default.createElement(Upload, { className: "mr-2 h-4 w-4" }), " Import ", metadataName)), /* @__PURE__ */ React119__namespace.default.createElement(DialogContent2, null, /* @__PURE__ */ React119__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React119__namespace.default.createElement(DialogTitle2, null, "Bulk Import ", metadataName), /* @__PURE__ */ React119__namespace.default.createElement(DialogDescription2, null, "Import multiple records from a file or by pasting JSON data.")), /* @__PURE__ */ React119__namespace.default.createElement(Tabs2, { defaultValue: "file" }, /* @__PURE__ */ React119__namespace.default.createElement(TabsList2, { className: "grid w-full grid-cols-2" }, /* @__PURE__ */ React119__namespace.default.createElement(TabsTrigger2, { value: "file" }, /* @__PURE__ */ React119__namespace.default.createElement(FileJson, { className: "mr-2 h-4 w-4" }), " From File"), /* @__PURE__ */ React119__namespace.default.createElement(TabsTrigger2, { value: "text" }, /* @__PURE__ */ React119__namespace.default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), " From Text")), /* @__PURE__ */ React119__namespace.default.createElement(TabsContent2, { value: "file", className: "pt-4" }, /* @__PURE__ */ React119__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React119__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, "Select a JSON or TSV/TXT file. Ensure column names in the file match the table schema (e.g., 'code', 'name', 'subjectCode')."), /* @__PURE__ */ React119__namespace.default.createElement(Button, { variant: "outline", onClick: () => fileInputRef.current?.click(), disabled: isImporting }, isImporting ? /* @__PURE__ */ React119__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React119__namespace.default.createElement(Upload, { className: "mr-2 h-4 w-4" }), isImporting ? "Importing..." : "Select File"), /* @__PURE__ */ React119__namespace.default.createElement("input", { type: "file", ref: fileInputRef, onChange: handleFileSelected, accept: ".json,.tsv,.txt,text/tab-separated-values", className: "hidden" }))), /* @__PURE__ */ React119__namespace.default.createElement(TabsContent2, { value: "text", className: "pt-4" }, /* @__PURE__ */ React119__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React119__namespace.default.createElement(Label2, { htmlFor: "jsonInput" }, "JSON Data (Array of Objects)"), /* @__PURE__ */ React119__namespace.default.createElement(
|
|
76494
76492
|
Textarea,
|
|
76495
76493
|
{
|
|
76496
76494
|
id: "jsonInput",
|
package/dist/authoring.mjs
CHANGED
|
@@ -8767,6 +8767,7 @@ init_react_shim();
|
|
|
8767
8767
|
// src/services/TopicDataService.ts
|
|
8768
8768
|
init_react_shim();
|
|
8769
8769
|
var TopicDataService = class {
|
|
8770
|
+
// ... saveData, mergeData, getData, clearData methods remain the same ...
|
|
8770
8771
|
static saveData(data) {
|
|
8771
8772
|
try {
|
|
8772
8773
|
if (typeof window === "undefined") return;
|
|
@@ -8811,47 +8812,41 @@ var TopicDataService = class {
|
|
|
8811
8812
|
}
|
|
8812
8813
|
const headerLine = lines.shift();
|
|
8813
8814
|
const headers = headerLine.split(" ").map((h2) => h2.trim());
|
|
8814
|
-
|
|
8815
|
-
const errorMsg = `Invalid TSV header. Expected: "${this.EXPECTED_HEADERS.join(" ")}". Received: "${headers.join(" ")}"`;
|
|
8816
|
-
return { data: [], errors: [errorMsg] };
|
|
8817
|
-
}
|
|
8815
|
+
const headerMap = headers.map((h2) => this.HEADER_MAP[h2] || null);
|
|
8818
8816
|
const data = [];
|
|
8819
8817
|
const errors2 = [];
|
|
8820
8818
|
lines.forEach((line, index3) => {
|
|
8821
|
-
const values = line.split(" ")
|
|
8822
|
-
|
|
8823
|
-
|
|
8819
|
+
const values = line.split(" ");
|
|
8820
|
+
const rowObject = {};
|
|
8821
|
+
headerMap.forEach((propName, i) => {
|
|
8822
|
+
if (propName) {
|
|
8823
|
+
const value = values[i]?.trim() || "";
|
|
8824
|
+
if (propName === "keywords" || propName === "stemElements" || propName === "bloomLevelsGuideline") {
|
|
8825
|
+
rowObject[propName] = value.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
8826
|
+
} else {
|
|
8827
|
+
rowObject[propName] = value;
|
|
8828
|
+
}
|
|
8829
|
+
}
|
|
8830
|
+
});
|
|
8831
|
+
if (!rowObject.code) {
|
|
8832
|
+
errors2.push(`Line ${index3 + 2}: Missing required value for 'LO ID'.`);
|
|
8824
8833
|
return;
|
|
8825
8834
|
}
|
|
8826
|
-
|
|
8827
|
-
|
|
8828
|
-
name3,
|
|
8829
|
-
loDescription,
|
|
8830
|
-
subject,
|
|
8831
|
-
category,
|
|
8832
|
-
topic,
|
|
8833
|
-
keywordsStr,
|
|
8834
|
-
grade,
|
|
8835
|
-
stemElementsStr,
|
|
8836
|
-
bloomLevelsStr
|
|
8837
|
-
] = values;
|
|
8838
|
-
if (!loId || !loDescription || !subject || !category || !topic) {
|
|
8839
|
-
errors2.push(`Line ${index3 + 2}: Missing required fields (LO ID, LO Description, Subject, Category, or Topic).`);
|
|
8840
|
-
return;
|
|
8835
|
+
if (!rowObject.name) {
|
|
8836
|
+
rowObject.name = rowObject.code;
|
|
8841
8837
|
}
|
|
8842
8838
|
const learningObjective = {
|
|
8843
8839
|
id: generateUniqueId("lo_"),
|
|
8844
|
-
code:
|
|
8845
|
-
name:
|
|
8846
|
-
description:
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
bloomLevelsGuideline: bloomLevelsStr.split(",").map((b) => b.trim()).filter(Boolean)
|
|
8840
|
+
code: rowObject.code,
|
|
8841
|
+
name: rowObject.name,
|
|
8842
|
+
description: rowObject.description,
|
|
8843
|
+
subject: rowObject.subject || "",
|
|
8844
|
+
category: rowObject.category || "",
|
|
8845
|
+
topic: rowObject.topic || "",
|
|
8846
|
+
grade: rowObject.grade || "",
|
|
8847
|
+
keywords: rowObject.keywords || [],
|
|
8848
|
+
stemElements: rowObject.stemElements || [],
|
|
8849
|
+
bloomLevelsGuideline: rowObject.bloomLevelsGuideline || []
|
|
8855
8850
|
};
|
|
8856
8851
|
data.push(learningObjective);
|
|
8857
8852
|
});
|
|
@@ -8879,17 +8874,20 @@ var TopicDataService = class {
|
|
|
8879
8874
|
}
|
|
8880
8875
|
};
|
|
8881
8876
|
TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
|
|
8882
|
-
|
|
8883
|
-
|
|
8884
|
-
"LO
|
|
8885
|
-
"
|
|
8886
|
-
|
|
8887
|
-
"
|
|
8888
|
-
"
|
|
8889
|
-
"
|
|
8890
|
-
"
|
|
8891
|
-
"
|
|
8892
|
-
|
|
8877
|
+
// Define a map for flexible header mapping
|
|
8878
|
+
TopicDataService.HEADER_MAP = {
|
|
8879
|
+
"LO ID": "code",
|
|
8880
|
+
"LO Name": "name",
|
|
8881
|
+
// Ready for future addition
|
|
8882
|
+
"LO Description": "description",
|
|
8883
|
+
"Subject": "subject",
|
|
8884
|
+
"Category": "category",
|
|
8885
|
+
"Topic": "topic",
|
|
8886
|
+
"Keywords": "keywords",
|
|
8887
|
+
"Grade": "grade",
|
|
8888
|
+
"STEM Element(s)": "stemElements",
|
|
8889
|
+
"Bloom\u2019s Level(s) Guideline": "bloomLevelsGuideline"
|
|
8890
|
+
};
|
|
8893
8891
|
|
|
8894
8892
|
// src/ai/flows/generate-questions-from-quiz-plan.ts
|
|
8895
8893
|
var MAX_ATTEMPTS = 3;
|
|
@@ -76378,8 +76376,8 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
76378
76376
|
const handleFileSelected = (event) => {
|
|
76379
76377
|
const file = event.target.files?.[0];
|
|
76380
76378
|
if (!file) return;
|
|
76381
|
-
if (file.type
|
|
76382
|
-
toast2({ title: "Invalid File Type", description: "Please select a JSON or
|
|
76379
|
+
if (!file.type.includes("json") && !file.type.includes("tab-separated-values") && !file.name.endsWith(".tsv") && !file.name.endsWith(".txt")) {
|
|
76380
|
+
toast2({ title: "Invalid File Type", description: "Please select a JSON or TSV/TXT file.", variant: "destructive" });
|
|
76383
76381
|
if (event.target) event.target.value = "";
|
|
76384
76382
|
return;
|
|
76385
76383
|
}
|
|
@@ -76387,18 +76385,18 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
76387
76385
|
try {
|
|
76388
76386
|
const fileContent = await file.text();
|
|
76389
76387
|
let records = [];
|
|
76390
|
-
if (file.type
|
|
76388
|
+
if (file.type.includes("json")) {
|
|
76391
76389
|
records = JSON.parse(fileContent);
|
|
76392
76390
|
if (!Array.isArray(records)) throw new Error("JSON file must contain an array of objects.");
|
|
76393
|
-
} else
|
|
76394
|
-
const lines = fileContent.split(/\r
|
|
76395
|
-
if (lines.length < 2) throw new Error("
|
|
76396
|
-
const headers = lines[0].split("
|
|
76391
|
+
} else {
|
|
76392
|
+
const lines = fileContent.split(/\r?\n/).filter((line) => line.trim() !== "");
|
|
76393
|
+
if (lines.length < 2) throw new Error("TSV file must have a header and at least one data row.");
|
|
76394
|
+
const headers = lines[0].split(" ").map((h2) => h2.trim());
|
|
76397
76395
|
records = lines.slice(1).map((line) => {
|
|
76398
|
-
const values = line.split("
|
|
76396
|
+
const values = line.split(" ");
|
|
76399
76397
|
const record = {};
|
|
76400
76398
|
headers.forEach((header, index3) => {
|
|
76401
|
-
record[header] = values[index3];
|
|
76399
|
+
record[header] = values[index3]?.trim() || "";
|
|
76402
76400
|
});
|
|
76403
76401
|
return record;
|
|
76404
76402
|
});
|
|
@@ -76425,7 +76423,7 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
76425
76423
|
}
|
|
76426
76424
|
});
|
|
76427
76425
|
};
|
|
76428
|
-
return /* @__PURE__ */ React119__default.createElement(Dialog2, { open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React119__default.createElement(DialogTrigger2, { asChild: true }, /* @__PURE__ */ React119__default.createElement(Button, { size: "sm", variant: "outline" }, /* @__PURE__ */ React119__default.createElement(Upload, { className: "mr-2 h-4 w-4" }), " Import ", metadataName)), /* @__PURE__ */ React119__default.createElement(DialogContent2, null, /* @__PURE__ */ React119__default.createElement(DialogHeader, null, /* @__PURE__ */ React119__default.createElement(DialogTitle2, null, "Bulk Import ", metadataName), /* @__PURE__ */ React119__default.createElement(DialogDescription2, null, "Import multiple records from a file or by pasting JSON data.")), /* @__PURE__ */ React119__default.createElement(Tabs2, { defaultValue: "file" }, /* @__PURE__ */ React119__default.createElement(TabsList2, { className: "grid w-full grid-cols-2" }, /* @__PURE__ */ React119__default.createElement(TabsTrigger2, { value: "file" }, /* @__PURE__ */ React119__default.createElement(FileJson, { className: "mr-2 h-4 w-4" }), " From File"), /* @__PURE__ */ React119__default.createElement(TabsTrigger2, { value: "text" }, /* @__PURE__ */ React119__default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), " From Text")), /* @__PURE__ */ React119__default.createElement(TabsContent2, { value: "file", className: "pt-4" }, /* @__PURE__ */ React119__default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React119__default.createElement("p", { className: "text-sm text-muted-foreground" }, "Select a JSON or
|
|
76426
|
+
return /* @__PURE__ */ React119__default.createElement(Dialog2, { open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React119__default.createElement(DialogTrigger2, { asChild: true }, /* @__PURE__ */ React119__default.createElement(Button, { size: "sm", variant: "outline" }, /* @__PURE__ */ React119__default.createElement(Upload, { className: "mr-2 h-4 w-4" }), " Import ", metadataName)), /* @__PURE__ */ React119__default.createElement(DialogContent2, null, /* @__PURE__ */ React119__default.createElement(DialogHeader, null, /* @__PURE__ */ React119__default.createElement(DialogTitle2, null, "Bulk Import ", metadataName), /* @__PURE__ */ React119__default.createElement(DialogDescription2, null, "Import multiple records from a file or by pasting JSON data.")), /* @__PURE__ */ React119__default.createElement(Tabs2, { defaultValue: "file" }, /* @__PURE__ */ React119__default.createElement(TabsList2, { className: "grid w-full grid-cols-2" }, /* @__PURE__ */ React119__default.createElement(TabsTrigger2, { value: "file" }, /* @__PURE__ */ React119__default.createElement(FileJson, { className: "mr-2 h-4 w-4" }), " From File"), /* @__PURE__ */ React119__default.createElement(TabsTrigger2, { value: "text" }, /* @__PURE__ */ React119__default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), " From Text")), /* @__PURE__ */ React119__default.createElement(TabsContent2, { value: "file", className: "pt-4" }, /* @__PURE__ */ React119__default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React119__default.createElement("p", { className: "text-sm text-muted-foreground" }, "Select a JSON or TSV/TXT file. Ensure column names in the file match the table schema (e.g., 'code', 'name', 'subjectCode')."), /* @__PURE__ */ React119__default.createElement(Button, { variant: "outline", onClick: () => fileInputRef.current?.click(), disabled: isImporting }, isImporting ? /* @__PURE__ */ React119__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React119__default.createElement(Upload, { className: "mr-2 h-4 w-4" }), isImporting ? "Importing..." : "Select File"), /* @__PURE__ */ React119__default.createElement("input", { type: "file", ref: fileInputRef, onChange: handleFileSelected, accept: ".json,.tsv,.txt,text/tab-separated-values", className: "hidden" }))), /* @__PURE__ */ React119__default.createElement(TabsContent2, { value: "text", className: "pt-4" }, /* @__PURE__ */ React119__default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React119__default.createElement(Label2, { htmlFor: "jsonInput" }, "JSON Data (Array of Objects)"), /* @__PURE__ */ React119__default.createElement(
|
|
76429
76427
|
Textarea,
|
|
76430
76428
|
{
|
|
76431
76429
|
id: "jsonInput",
|
package/dist/react-ui.cjs
CHANGED
|
@@ -101917,6 +101917,7 @@ init_react_shim();
|
|
|
101917
101917
|
// src/services/TopicDataService.ts
|
|
101918
101918
|
init_react_shim();
|
|
101919
101919
|
var TopicDataService = class {
|
|
101920
|
+
// ... saveData, mergeData, getData, clearData methods remain the same ...
|
|
101920
101921
|
static saveData(data) {
|
|
101921
101922
|
try {
|
|
101922
101923
|
if (typeof window === "undefined") return;
|
|
@@ -101961,47 +101962,41 @@ var TopicDataService = class {
|
|
|
101961
101962
|
}
|
|
101962
101963
|
const headerLine = lines.shift();
|
|
101963
101964
|
const headers = headerLine.split(" ").map((h3) => h3.trim());
|
|
101964
|
-
|
|
101965
|
-
const errorMsg = `Invalid TSV header. Expected: "${this.EXPECTED_HEADERS.join(" ")}". Received: "${headers.join(" ")}"`;
|
|
101966
|
-
return { data: [], errors: [errorMsg] };
|
|
101967
|
-
}
|
|
101965
|
+
const headerMap = headers.map((h3) => this.HEADER_MAP[h3] || null);
|
|
101968
101966
|
const data = [];
|
|
101969
101967
|
const errors2 = [];
|
|
101970
101968
|
lines.forEach((line, index3) => {
|
|
101971
|
-
const values = line.split(" ")
|
|
101972
|
-
|
|
101973
|
-
|
|
101969
|
+
const values = line.split(" ");
|
|
101970
|
+
const rowObject = {};
|
|
101971
|
+
headerMap.forEach((propName, i2) => {
|
|
101972
|
+
if (propName) {
|
|
101973
|
+
const value = values[i2]?.trim() || "";
|
|
101974
|
+
if (propName === "keywords" || propName === "stemElements" || propName === "bloomLevelsGuideline") {
|
|
101975
|
+
rowObject[propName] = value.split(",").map((s4) => s4.trim()).filter(Boolean);
|
|
101976
|
+
} else {
|
|
101977
|
+
rowObject[propName] = value;
|
|
101978
|
+
}
|
|
101979
|
+
}
|
|
101980
|
+
});
|
|
101981
|
+
if (!rowObject.code) {
|
|
101982
|
+
errors2.push(`Line ${index3 + 2}: Missing required value for 'LO ID'.`);
|
|
101974
101983
|
return;
|
|
101975
101984
|
}
|
|
101976
|
-
|
|
101977
|
-
|
|
101978
|
-
name3,
|
|
101979
|
-
loDescription,
|
|
101980
|
-
subject,
|
|
101981
|
-
category,
|
|
101982
|
-
topic,
|
|
101983
|
-
keywordsStr,
|
|
101984
|
-
grade,
|
|
101985
|
-
stemElementsStr,
|
|
101986
|
-
bloomLevelsStr
|
|
101987
|
-
] = values;
|
|
101988
|
-
if (!loId || !loDescription || !subject || !category || !topic) {
|
|
101989
|
-
errors2.push(`Line ${index3 + 2}: Missing required fields (LO ID, LO Description, Subject, Category, or Topic).`);
|
|
101990
|
-
return;
|
|
101985
|
+
if (!rowObject.name) {
|
|
101986
|
+
rowObject.name = rowObject.code;
|
|
101991
101987
|
}
|
|
101992
101988
|
const learningObjective = {
|
|
101993
101989
|
id: generateUniqueId("lo_"),
|
|
101994
|
-
code:
|
|
101995
|
-
name:
|
|
101996
|
-
description:
|
|
101997
|
-
|
|
101998
|
-
|
|
101999
|
-
|
|
102000
|
-
|
|
102001
|
-
|
|
102002
|
-
|
|
102003
|
-
|
|
102004
|
-
bloomLevelsGuideline: bloomLevelsStr.split(",").map((b2) => b2.trim()).filter(Boolean)
|
|
101990
|
+
code: rowObject.code,
|
|
101991
|
+
name: rowObject.name,
|
|
101992
|
+
description: rowObject.description,
|
|
101993
|
+
subject: rowObject.subject || "",
|
|
101994
|
+
category: rowObject.category || "",
|
|
101995
|
+
topic: rowObject.topic || "",
|
|
101996
|
+
grade: rowObject.grade || "",
|
|
101997
|
+
keywords: rowObject.keywords || [],
|
|
101998
|
+
stemElements: rowObject.stemElements || [],
|
|
101999
|
+
bloomLevelsGuideline: rowObject.bloomLevelsGuideline || []
|
|
102005
102000
|
};
|
|
102006
102001
|
data.push(learningObjective);
|
|
102007
102002
|
});
|
|
@@ -102029,17 +102024,20 @@ var TopicDataService = class {
|
|
|
102029
102024
|
}
|
|
102030
102025
|
};
|
|
102031
102026
|
TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
|
|
102032
|
-
|
|
102033
|
-
|
|
102034
|
-
"LO
|
|
102035
|
-
"
|
|
102036
|
-
|
|
102037
|
-
"
|
|
102038
|
-
"
|
|
102039
|
-
"
|
|
102040
|
-
"
|
|
102041
|
-
"
|
|
102042
|
-
|
|
102027
|
+
// Define a map for flexible header mapping
|
|
102028
|
+
TopicDataService.HEADER_MAP = {
|
|
102029
|
+
"LO ID": "code",
|
|
102030
|
+
"LO Name": "name",
|
|
102031
|
+
// Ready for future addition
|
|
102032
|
+
"LO Description": "description",
|
|
102033
|
+
"Subject": "subject",
|
|
102034
|
+
"Category": "category",
|
|
102035
|
+
"Topic": "topic",
|
|
102036
|
+
"Keywords": "keywords",
|
|
102037
|
+
"Grade": "grade",
|
|
102038
|
+
"STEM Element(s)": "stemElements",
|
|
102039
|
+
"Bloom\u2019s Level(s) Guideline": "bloomLevelsGuideline"
|
|
102040
|
+
};
|
|
102043
102041
|
|
|
102044
102042
|
// src/ai/flows/question-gen/generate-coding-question.ts
|
|
102045
102043
|
init_react_shim();
|
|
@@ -138739,8 +138737,8 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
138739
138737
|
const handleFileSelected = (event) => {
|
|
138740
138738
|
const file = event.target.files?.[0];
|
|
138741
138739
|
if (!file) return;
|
|
138742
|
-
if (file.type
|
|
138743
|
-
toast2({ title: "Invalid File Type", description: "Please select a JSON or
|
|
138740
|
+
if (!file.type.includes("json") && !file.type.includes("tab-separated-values") && !file.name.endsWith(".tsv") && !file.name.endsWith(".txt")) {
|
|
138741
|
+
toast2({ title: "Invalid File Type", description: "Please select a JSON or TSV/TXT file.", variant: "destructive" });
|
|
138744
138742
|
if (event.target) event.target.value = "";
|
|
138745
138743
|
return;
|
|
138746
138744
|
}
|
|
@@ -138748,18 +138746,18 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
138748
138746
|
try {
|
|
138749
138747
|
const fileContent = await file.text();
|
|
138750
138748
|
let records = [];
|
|
138751
|
-
if (file.type
|
|
138749
|
+
if (file.type.includes("json")) {
|
|
138752
138750
|
records = JSON.parse(fileContent);
|
|
138753
138751
|
if (!Array.isArray(records)) throw new Error("JSON file must contain an array of objects.");
|
|
138754
|
-
} else
|
|
138755
|
-
const lines = fileContent.split(/\r
|
|
138756
|
-
if (lines.length < 2) throw new Error("
|
|
138757
|
-
const headers = lines[0].split("
|
|
138752
|
+
} else {
|
|
138753
|
+
const lines = fileContent.split(/\r?\n/).filter((line) => line.trim() !== "");
|
|
138754
|
+
if (lines.length < 2) throw new Error("TSV file must have a header and at least one data row.");
|
|
138755
|
+
const headers = lines[0].split(" ").map((h3) => h3.trim());
|
|
138758
138756
|
records = lines.slice(1).map((line) => {
|
|
138759
|
-
const values = line.split("
|
|
138757
|
+
const values = line.split(" ");
|
|
138760
138758
|
const record = {};
|
|
138761
138759
|
headers.forEach((header, index3) => {
|
|
138762
|
-
record[header] = values[index3];
|
|
138760
|
+
record[header] = values[index3]?.trim() || "";
|
|
138763
138761
|
});
|
|
138764
138762
|
return record;
|
|
138765
138763
|
});
|
|
@@ -138786,7 +138784,7 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
138786
138784
|
}
|
|
138787
138785
|
});
|
|
138788
138786
|
};
|
|
138789
|
-
return /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", variant: "outline" }, /* @__PURE__ */ React169__namespace.default.createElement(Upload, { className: "mr-2 h-4 w-4" }), " Import ", metadataName)), /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, "Bulk Import ", metadataName), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, "Import multiple records from a file or by pasting JSON data.")), /* @__PURE__ */ React169__namespace.default.createElement(Tabs2, { defaultValue: "file" }, /* @__PURE__ */ React169__namespace.default.createElement(TabsList2, { className: "grid w-full grid-cols-2" }, /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "file" }, /* @__PURE__ */ React169__namespace.default.createElement(FileJson, { className: "mr-2 h-4 w-4" }), " From File"), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "text" }, /* @__PURE__ */ React169__namespace.default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), " From Text")), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "file", className: "pt-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, "Select a JSON or
|
|
138787
|
+
return /* @__PURE__ */ React169__namespace.default.createElement(Dialog2, { open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React169__namespace.default.createElement(DialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__namespace.default.createElement(Button, { size: "sm", variant: "outline" }, /* @__PURE__ */ React169__namespace.default.createElement(Upload, { className: "mr-2 h-4 w-4" }), " Import ", metadataName)), /* @__PURE__ */ React169__namespace.default.createElement(DialogContent2, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogHeader, null, /* @__PURE__ */ React169__namespace.default.createElement(DialogTitle2, null, "Bulk Import ", metadataName), /* @__PURE__ */ React169__namespace.default.createElement(DialogDescription2, null, "Import multiple records from a file or by pasting JSON data.")), /* @__PURE__ */ React169__namespace.default.createElement(Tabs2, { defaultValue: "file" }, /* @__PURE__ */ React169__namespace.default.createElement(TabsList2, { className: "grid w-full grid-cols-2" }, /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "file" }, /* @__PURE__ */ React169__namespace.default.createElement(FileJson, { className: "mr-2 h-4 w-4" }), " From File"), /* @__PURE__ */ React169__namespace.default.createElement(TabsTrigger2, { value: "text" }, /* @__PURE__ */ React169__namespace.default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), " From Text")), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "file", className: "pt-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__namespace.default.createElement("p", { className: "text-sm text-muted-foreground" }, "Select a JSON or TSV/TXT file. Ensure column names in the file match the table schema (e.g., 'code', 'name', 'subjectCode')."), /* @__PURE__ */ React169__namespace.default.createElement(Button, { variant: "outline", onClick: () => fileInputRef.current?.click(), disabled: isImporting }, isImporting ? /* @__PURE__ */ React169__namespace.default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__namespace.default.createElement(Upload, { className: "mr-2 h-4 w-4" }), isImporting ? "Importing..." : "Select File"), /* @__PURE__ */ React169__namespace.default.createElement("input", { type: "file", ref: fileInputRef, onChange: handleFileSelected, accept: ".json,.tsv,.txt,text/tab-separated-values", className: "hidden" }))), /* @__PURE__ */ React169__namespace.default.createElement(TabsContent2, { value: "text", className: "pt-4" }, /* @__PURE__ */ React169__namespace.default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__namespace.default.createElement(Label2, { htmlFor: "jsonInput" }, "JSON Data (Array of Objects)"), /* @__PURE__ */ React169__namespace.default.createElement(
|
|
138790
138788
|
Textarea,
|
|
138791
138789
|
{
|
|
138792
138790
|
id: "jsonInput",
|
package/dist/react-ui.mjs
CHANGED
|
@@ -101851,6 +101851,7 @@ init_react_shim();
|
|
|
101851
101851
|
// src/services/TopicDataService.ts
|
|
101852
101852
|
init_react_shim();
|
|
101853
101853
|
var TopicDataService = class {
|
|
101854
|
+
// ... saveData, mergeData, getData, clearData methods remain the same ...
|
|
101854
101855
|
static saveData(data) {
|
|
101855
101856
|
try {
|
|
101856
101857
|
if (typeof window === "undefined") return;
|
|
@@ -101895,47 +101896,41 @@ var TopicDataService = class {
|
|
|
101895
101896
|
}
|
|
101896
101897
|
const headerLine = lines.shift();
|
|
101897
101898
|
const headers = headerLine.split(" ").map((h3) => h3.trim());
|
|
101898
|
-
|
|
101899
|
-
const errorMsg = `Invalid TSV header. Expected: "${this.EXPECTED_HEADERS.join(" ")}". Received: "${headers.join(" ")}"`;
|
|
101900
|
-
return { data: [], errors: [errorMsg] };
|
|
101901
|
-
}
|
|
101899
|
+
const headerMap = headers.map((h3) => this.HEADER_MAP[h3] || null);
|
|
101902
101900
|
const data = [];
|
|
101903
101901
|
const errors2 = [];
|
|
101904
101902
|
lines.forEach((line, index3) => {
|
|
101905
|
-
const values = line.split(" ")
|
|
101906
|
-
|
|
101907
|
-
|
|
101903
|
+
const values = line.split(" ");
|
|
101904
|
+
const rowObject = {};
|
|
101905
|
+
headerMap.forEach((propName, i2) => {
|
|
101906
|
+
if (propName) {
|
|
101907
|
+
const value = values[i2]?.trim() || "";
|
|
101908
|
+
if (propName === "keywords" || propName === "stemElements" || propName === "bloomLevelsGuideline") {
|
|
101909
|
+
rowObject[propName] = value.split(",").map((s4) => s4.trim()).filter(Boolean);
|
|
101910
|
+
} else {
|
|
101911
|
+
rowObject[propName] = value;
|
|
101912
|
+
}
|
|
101913
|
+
}
|
|
101914
|
+
});
|
|
101915
|
+
if (!rowObject.code) {
|
|
101916
|
+
errors2.push(`Line ${index3 + 2}: Missing required value for 'LO ID'.`);
|
|
101908
101917
|
return;
|
|
101909
101918
|
}
|
|
101910
|
-
|
|
101911
|
-
|
|
101912
|
-
name3,
|
|
101913
|
-
loDescription,
|
|
101914
|
-
subject,
|
|
101915
|
-
category,
|
|
101916
|
-
topic,
|
|
101917
|
-
keywordsStr,
|
|
101918
|
-
grade,
|
|
101919
|
-
stemElementsStr,
|
|
101920
|
-
bloomLevelsStr
|
|
101921
|
-
] = values;
|
|
101922
|
-
if (!loId || !loDescription || !subject || !category || !topic) {
|
|
101923
|
-
errors2.push(`Line ${index3 + 2}: Missing required fields (LO ID, LO Description, Subject, Category, or Topic).`);
|
|
101924
|
-
return;
|
|
101919
|
+
if (!rowObject.name) {
|
|
101920
|
+
rowObject.name = rowObject.code;
|
|
101925
101921
|
}
|
|
101926
101922
|
const learningObjective = {
|
|
101927
101923
|
id: generateUniqueId("lo_"),
|
|
101928
|
-
code:
|
|
101929
|
-
name:
|
|
101930
|
-
description:
|
|
101931
|
-
|
|
101932
|
-
|
|
101933
|
-
|
|
101934
|
-
|
|
101935
|
-
|
|
101936
|
-
|
|
101937
|
-
|
|
101938
|
-
bloomLevelsGuideline: bloomLevelsStr.split(",").map((b2) => b2.trim()).filter(Boolean)
|
|
101924
|
+
code: rowObject.code,
|
|
101925
|
+
name: rowObject.name,
|
|
101926
|
+
description: rowObject.description,
|
|
101927
|
+
subject: rowObject.subject || "",
|
|
101928
|
+
category: rowObject.category || "",
|
|
101929
|
+
topic: rowObject.topic || "",
|
|
101930
|
+
grade: rowObject.grade || "",
|
|
101931
|
+
keywords: rowObject.keywords || [],
|
|
101932
|
+
stemElements: rowObject.stemElements || [],
|
|
101933
|
+
bloomLevelsGuideline: rowObject.bloomLevelsGuideline || []
|
|
101939
101934
|
};
|
|
101940
101935
|
data.push(learningObjective);
|
|
101941
101936
|
});
|
|
@@ -101963,17 +101958,20 @@ var TopicDataService = class {
|
|
|
101963
101958
|
}
|
|
101964
101959
|
};
|
|
101965
101960
|
TopicDataService.STORAGE_KEY = "interactive_quiz_kit_learning_objectives";
|
|
101966
|
-
|
|
101967
|
-
|
|
101968
|
-
"LO
|
|
101969
|
-
"
|
|
101970
|
-
|
|
101971
|
-
"
|
|
101972
|
-
"
|
|
101973
|
-
"
|
|
101974
|
-
"
|
|
101975
|
-
"
|
|
101976
|
-
|
|
101961
|
+
// Define a map for flexible header mapping
|
|
101962
|
+
TopicDataService.HEADER_MAP = {
|
|
101963
|
+
"LO ID": "code",
|
|
101964
|
+
"LO Name": "name",
|
|
101965
|
+
// Ready for future addition
|
|
101966
|
+
"LO Description": "description",
|
|
101967
|
+
"Subject": "subject",
|
|
101968
|
+
"Category": "category",
|
|
101969
|
+
"Topic": "topic",
|
|
101970
|
+
"Keywords": "keywords",
|
|
101971
|
+
"Grade": "grade",
|
|
101972
|
+
"STEM Element(s)": "stemElements",
|
|
101973
|
+
"Bloom\u2019s Level(s) Guideline": "bloomLevelsGuideline"
|
|
101974
|
+
};
|
|
101977
101975
|
|
|
101978
101976
|
// src/ai/flows/question-gen/generate-coding-question.ts
|
|
101979
101977
|
init_react_shim();
|
|
@@ -138673,8 +138671,8 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
138673
138671
|
const handleFileSelected = (event) => {
|
|
138674
138672
|
const file = event.target.files?.[0];
|
|
138675
138673
|
if (!file) return;
|
|
138676
|
-
if (file.type
|
|
138677
|
-
toast2({ title: "Invalid File Type", description: "Please select a JSON or
|
|
138674
|
+
if (!file.type.includes("json") && !file.type.includes("tab-separated-values") && !file.name.endsWith(".tsv") && !file.name.endsWith(".txt")) {
|
|
138675
|
+
toast2({ title: "Invalid File Type", description: "Please select a JSON or TSV/TXT file.", variant: "destructive" });
|
|
138678
138676
|
if (event.target) event.target.value = "";
|
|
138679
138677
|
return;
|
|
138680
138678
|
}
|
|
@@ -138682,18 +138680,18 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
138682
138680
|
try {
|
|
138683
138681
|
const fileContent = await file.text();
|
|
138684
138682
|
let records = [];
|
|
138685
|
-
if (file.type
|
|
138683
|
+
if (file.type.includes("json")) {
|
|
138686
138684
|
records = JSON.parse(fileContent);
|
|
138687
138685
|
if (!Array.isArray(records)) throw new Error("JSON file must contain an array of objects.");
|
|
138688
|
-
} else
|
|
138689
|
-
const lines = fileContent.split(/\r
|
|
138690
|
-
if (lines.length < 2) throw new Error("
|
|
138691
|
-
const headers = lines[0].split("
|
|
138686
|
+
} else {
|
|
138687
|
+
const lines = fileContent.split(/\r?\n/).filter((line) => line.trim() !== "");
|
|
138688
|
+
if (lines.length < 2) throw new Error("TSV file must have a header and at least one data row.");
|
|
138689
|
+
const headers = lines[0].split(" ").map((h3) => h3.trim());
|
|
138692
138690
|
records = lines.slice(1).map((line) => {
|
|
138693
|
-
const values = line.split("
|
|
138691
|
+
const values = line.split(" ");
|
|
138694
138692
|
const record = {};
|
|
138695
138693
|
headers.forEach((header, index3) => {
|
|
138696
|
-
record[header] = values[index3];
|
|
138694
|
+
record[header] = values[index3]?.trim() || "";
|
|
138697
138695
|
});
|
|
138698
138696
|
return record;
|
|
138699
138697
|
});
|
|
@@ -138720,7 +138718,7 @@ function MetadataImportControls({ metadataName, onImport }) {
|
|
|
138720
138718
|
}
|
|
138721
138719
|
});
|
|
138722
138720
|
};
|
|
138723
|
-
return /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React169__default.createElement(DialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", variant: "outline" }, /* @__PURE__ */ React169__default.createElement(Upload, { className: "mr-2 h-4 w-4" }), " Import ", metadataName)), /* @__PURE__ */ React169__default.createElement(DialogContent2, null, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, "Bulk Import ", metadataName), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, "Import multiple records from a file or by pasting JSON data.")), /* @__PURE__ */ React169__default.createElement(Tabs2, { defaultValue: "file" }, /* @__PURE__ */ React169__default.createElement(TabsList2, { className: "grid w-full grid-cols-2" }, /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "file" }, /* @__PURE__ */ React169__default.createElement(FileJson, { className: "mr-2 h-4 w-4" }), " From File"), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "text" }, /* @__PURE__ */ React169__default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), " From Text")), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "file", className: "pt-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, "Select a JSON or
|
|
138721
|
+
return /* @__PURE__ */ React169__default.createElement(Dialog2, { open: isOpen, onOpenChange: setIsOpen }, /* @__PURE__ */ React169__default.createElement(DialogTrigger2, { asChild: true }, /* @__PURE__ */ React169__default.createElement(Button, { size: "sm", variant: "outline" }, /* @__PURE__ */ React169__default.createElement(Upload, { className: "mr-2 h-4 w-4" }), " Import ", metadataName)), /* @__PURE__ */ React169__default.createElement(DialogContent2, null, /* @__PURE__ */ React169__default.createElement(DialogHeader, null, /* @__PURE__ */ React169__default.createElement(DialogTitle2, null, "Bulk Import ", metadataName), /* @__PURE__ */ React169__default.createElement(DialogDescription2, null, "Import multiple records from a file or by pasting JSON data.")), /* @__PURE__ */ React169__default.createElement(Tabs2, { defaultValue: "file" }, /* @__PURE__ */ React169__default.createElement(TabsList2, { className: "grid w-full grid-cols-2" }, /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "file" }, /* @__PURE__ */ React169__default.createElement(FileJson, { className: "mr-2 h-4 w-4" }), " From File"), /* @__PURE__ */ React169__default.createElement(TabsTrigger2, { value: "text" }, /* @__PURE__ */ React169__default.createElement(ClipboardPaste, { className: "mr-2 h-4 w-4" }), " From Text")), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "file", className: "pt-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__default.createElement("p", { className: "text-sm text-muted-foreground" }, "Select a JSON or TSV/TXT file. Ensure column names in the file match the table schema (e.g., 'code', 'name', 'subjectCode')."), /* @__PURE__ */ React169__default.createElement(Button, { variant: "outline", onClick: () => fileInputRef.current?.click(), disabled: isImporting }, isImporting ? /* @__PURE__ */ React169__default.createElement(LoaderCircle, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ React169__default.createElement(Upload, { className: "mr-2 h-4 w-4" }), isImporting ? "Importing..." : "Select File"), /* @__PURE__ */ React169__default.createElement("input", { type: "file", ref: fileInputRef, onChange: handleFileSelected, accept: ".json,.tsv,.txt,text/tab-separated-values", className: "hidden" }))), /* @__PURE__ */ React169__default.createElement(TabsContent2, { value: "text", className: "pt-4" }, /* @__PURE__ */ React169__default.createElement("div", { className: "space-y-3" }, /* @__PURE__ */ React169__default.createElement(Label2, { htmlFor: "jsonInput" }, "JSON Data (Array of Objects)"), /* @__PURE__ */ React169__default.createElement(
|
|
138724
138722
|
Textarea,
|
|
138725
138723
|
{
|
|
138726
138724
|
id: "jsonInput",
|
package/package.json
CHANGED