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