@defikitdotnet/education-module-ai
Version:
AI Education Module using Agent Framework
283 lines (282 loc) • 21.1 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CategoryManager = void 0;
var jsx_runtime_1 = require("react/jsx-runtime");
var react_1 = require("react");
var button_1 = require("../components/ui/button");
var input_1 = require("../components/ui/input");
var label_1 = require("../components/ui/label");
var textarea_1 = require("../components/ui/textarea");
var lucide_react_1 = require("lucide-react");
var checkbox_1 = require("../components/ui/checkbox");
var dialog_1 = require("../components/ui/dialog");
var CategoryManager = function (_a) {
var _b, _c;
var onCategoriesChange = _a.onCategoriesChange, _d = _a.selectedCategories, selectedCategories = _d === void 0 ? [] : _d, _e = _a.mode, mode = _e === void 0 ? "select" : _e, teacherId = _a.teacherId, apiBaseUrl = _a.apiBaseUrl, _f = _a.disabled, disabled = _f === void 0 ? false : _f;
var _g = (0, react_1.useState)([]), categories = _g[0], setCategories = _g[1];
var _h = (0, react_1.useState)(""), newCategoryName = _h[0], setNewCategoryName = _h[1];
var _j = (0, react_1.useState)(""), newCategoryDescription = _j[0], setNewCategoryDescription = _j[1];
var _k = (0, react_1.useState)(null), error = _k[0], setError = _k[1];
var _l = (0, react_1.useState)(false), loading = _l[0], setLoading = _l[1];
var _m = (0, react_1.useState)(selectedCategories), selected = _m[0], setSelected = _m[1];
var _o = (0, react_1.useState)(false), showAddCategoryModal = _o[0], setShowAddCategoryModal = _o[1];
var _p = (0, react_1.useState)(false), isSavingCategory = _p[0], setIsSavingCategory = _p[1];
var _q = (0, react_1.useState)(null), categoryError = _q[0], setCategoryError = _q[1];
var _r = (0, react_1.useState)(null), confirmDeleteCategoryId = _r[0], setConfirmDeleteCategoryId = _r[1];
var _s = (0, react_1.useState)(false), isDeletingCategory = _s[0], setIsDeletingCategory = _s[1];
// Fetch categories
var fetchCategories = function () { return __awaiter(void 0, void 0, void 0, function () {
var endpoint, response, errorMsg, errorData, parseError_1, data, err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!teacherId)
return [2 /*return*/];
_a.label = 1;
case 1:
_a.trys.push([1, 9, 10, 11]);
setLoading(true);
setError(null);
endpoint = "".concat(apiBaseUrl, "/api/categories/teacher/").concat(teacherId);
return [4 /*yield*/, fetch(endpoint)];
case 2:
response = _a.sent();
if (!!response.ok) return [3 /*break*/, 7];
errorMsg = "Failed to fetch categories";
_a.label = 3;
case 3:
_a.trys.push([3, 5, , 6]);
return [4 /*yield*/, response.json()];
case 4:
errorData = _a.sent();
errorMsg = errorData.error || errorMsg;
return [3 /*break*/, 6];
case 5:
parseError_1 = _a.sent();
return [3 /*break*/, 6];
case 6: throw new Error(errorMsg);
case 7: return [4 /*yield*/, response.json()];
case 8:
data = _a.sent();
setCategories((data || []).sort(function (a, b) { return a.name.localeCompare(b.name); }));
return [3 /*break*/, 11];
case 9:
err_1 = _a.sent();
console.error("Error fetching categories:", err_1);
setError(err_1 instanceof Error ? err_1.message : "Failed to load categories. Please try again.");
return [3 /*break*/, 11];
case 10:
setLoading(false);
return [7 /*endfinally*/];
case 11: return [2 /*return*/];
}
});
}); };
// Initial fetch
(0, react_1.useEffect)(function () {
fetchCategories();
}, [teacherId, apiBaseUrl]);
// Sync selected categories from props
(0, react_1.useEffect)(function () {
setSelected(selectedCategories);
}, [selectedCategories]);
// Handle category selection
var handleCategorySelect = function (categoryId) {
if (mode !== "select" || disabled)
return;
var newSelected = selected.includes(categoryId)
? selected.filter(function (id) { return id !== categoryId; })
: __spreadArray(__spreadArray([], selected, true), [categoryId], false);
setSelected(newSelected);
if (onCategoriesChange) {
onCategoriesChange(newSelected);
}
};
// Handle opening the Add Category modal
var handleOpenAddModal = function () {
setNewCategoryName("");
setNewCategoryDescription("");
setCategoryError(null);
};
// Add a new category
var handleAddCategory = function () { return __awaiter(void 0, void 0, void 0, function () {
var requestBody, response, errorData, newCategory_1, newSelected, err_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!teacherId || !newCategoryName.trim()) {
setCategoryError("Category name is required");
return [2 /*return*/];
}
if (disabled)
return [2 /*return*/];
_a.label = 1;
case 1:
_a.trys.push([1, 6, 7, 8]);
setError(null);
setCategoryError(null);
setIsSavingCategory(true);
requestBody = {
teacherId: teacherId,
name: newCategoryName.trim(),
description: newCategoryDescription.trim() || undefined,
};
return [4 /*yield*/, fetch("".concat(apiBaseUrl, "/api/categories"), {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(requestBody),
})];
case 2:
response = _a.sent();
if (!!response.ok) return [3 /*break*/, 4];
return [4 /*yield*/, response.json()];
case 3:
errorData = _a.sent();
throw new Error(errorData.error || "Failed to create category");
case 4: return [4 /*yield*/, response.json()];
case 5:
newCategory_1 = _a.sent();
// Update state and re-sort
setCategories(function (prev) { return __spreadArray(__spreadArray([], prev, true), [newCategory_1], false).sort(function (a, b) { return a.name.localeCompare(b.name); }); });
// Automatically select the new category if in 'select' mode
if (mode === "select") {
newSelected = __spreadArray(__spreadArray([], selected, true), [newCategory_1.id], false);
setSelected(newSelected);
if (onCategoriesChange) {
onCategoriesChange(newSelected);
}
}
setShowAddCategoryModal(false);
return [3 /*break*/, 8];
case 6:
err_2 = _a.sent();
console.error("Error creating category:", err_2);
setCategoryError(err_2.message || "Failed to create category");
return [3 /*break*/, 8];
case 7:
setIsSavingCategory(false);
return [7 /*endfinally*/];
case 8: return [2 /*return*/];
}
});
}); };
// Open confirmation dialog
var handleDeleteClick = function (categoryId) {
if (disabled)
return;
setConfirmDeleteCategoryId(categoryId);
};
// Handle the actual deletion after confirmation
var handleConfirmDelete = function () { return __awaiter(void 0, void 0, void 0, function () {
var response, errorMsg, errorData, parseError_2, updatedCategories, newSelected, err_3;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!confirmDeleteCategoryId || !teacherId || disabled)
return [2 /*return*/];
setIsDeletingCategory(true);
setError(null);
_a.label = 1;
case 1:
_a.trys.push([1, 8, 9, 10]);
return [4 /*yield*/, fetch("".concat(apiBaseUrl, "/api/categories/").concat(confirmDeleteCategoryId), {
method: "DELETE",
headers: {
"Content-Type": "application/json"
}
})];
case 2:
response = _a.sent();
if (!!response.ok) return [3 /*break*/, 7];
errorMsg = "Failed to delete category";
_a.label = 3;
case 3:
_a.trys.push([3, 5, , 6]);
return [4 /*yield*/, response.json()];
case 4:
errorData = _a.sent();
errorMsg = errorData.error || errorMsg;
return [3 /*break*/, 6];
case 5:
parseError_2 = _a.sent();
return [3 /*break*/, 6];
case 6: throw new Error(errorMsg);
case 7:
updatedCategories = categories.filter(function (cat) { return cat.id !== confirmDeleteCategoryId; });
setCategories(updatedCategories);
newSelected = selected.filter(function (id) { return id !== confirmDeleteCategoryId; });
if (newSelected.length !== selected.length) {
setSelected(newSelected);
if (onCategoriesChange) {
onCategoriesChange(newSelected);
}
}
// Close confirmation dialog
setConfirmDeleteCategoryId(null);
return [3 /*break*/, 10];
case 8:
err_3 = _a.sent();
console.error("Error deleting category:", err_3);
setError(err_3.message || "Failed to delete category");
return [3 /*break*/, 10];
case 9:
setIsDeletingCategory(false);
return [7 /*endfinally*/];
case 10: return [2 /*return*/];
}
});
}); };
if (loading && categories.length === 0) {
return ((0, jsx_runtime_1.jsxs)("div", { className: "text-center py-4 px-6 bg-white rounded-lg shadow", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "animate-spin h-6 w-6 text-indigo-600 mx-auto" }), (0, jsx_runtime_1.jsx)("p", { className: "mt-2 text-sm text-gray-600", children: "Loading categories..." })] }));
}
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)(dialog_1.Dialog, { open: showAddCategoryModal, onOpenChange: setShowAddCategoryModal, children: [(0, jsx_runtime_1.jsxs)("div", { className: "bg-white rounded-lg shadow", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center p-4 border-b border-gray-200", children: [(0, jsx_runtime_1.jsx)("h3", { className: "text-lg font-semibold text-gray-800", children: "Select Categories" }), (0, jsx_runtime_1.jsx)(dialog_1.DialogTrigger, { asChild: true, children: (0, jsx_runtime_1.jsxs)(button_1.Button, { variant: "outline", size: "sm", onClick: handleOpenAddModal, disabled: disabled || loading || !teacherId, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.PlusCircle, { className: "mr-2 h-4 w-4" }), " Add Category"] }) })] }), error && ((0, jsx_runtime_1.jsxs)("div", { className: "p-4 text-sm text-red-600 bg-red-50 border-b border-red-200", children: ["Error: ", error] })), (0, jsx_runtime_1.jsx)("div", { className: "p-4 max-h-60 overflow-y-auto", children: categories.length > 0 ? ((0, jsx_runtime_1.jsx)("ul", { className: "space-y-2", children: categories.map(function (category) { return ((0, jsx_runtime_1.jsxs)("li", { className: "flex items-center justify-between py-1 border-b border-gray-100 last:border-b-0", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center space-x-3 flex-grow mr-2", children: [(0, jsx_runtime_1.jsx)(checkbox_1.Checkbox, { id: "category-".concat(category.id), checked: selected.includes(category.id), onCheckedChange: function () { return handleCategorySelect(category.id); }, disabled: disabled, "aria-labelledby": "category-label-".concat(category.id) }), (0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "category-".concat(category.id), id: "category-label-".concat(category.id), className: "text-sm ".concat(disabled ? 'text-gray-400 cursor-not-allowed' : 'text-gray-700 cursor-pointer'), children: category.name })] }), (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "ghost", size: "icon", className: "h-7 w-7 text-gray-400 hover:text-red-600 hover:bg-red-50", onClick: function () { return handleDeleteClick(category.id); }, disabled: disabled || isDeletingCategory, "aria-label": "Delete category ".concat(category.name), title: "Delete category ".concat(category.name), children: (0, jsx_runtime_1.jsx)(lucide_react_1.Trash2, { className: "h-4 w-4" }) })] }, category.id)); }) })) : ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 text-center py-4", children: loading ? "Loading..." : (teacherId ? "No categories found." : "Authentication required.") })) })] }), (0, jsx_runtime_1.jsxs)(dialog_1.DialogContent, { className: "sm:max-w-[425px] bg-white", children: [(0, jsx_runtime_1.jsxs)(dialog_1.DialogHeader, { children: [(0, jsx_runtime_1.jsx)(dialog_1.DialogTitle, { children: "Add New Category" }), (0, jsx_runtime_1.jsx)(dialog_1.DialogDescription, { children: "Enter the details for the new category." })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid gap-4 py-4", children: [categoryError && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-red-600 bg-red-50 p-2 rounded border border-red-200", children: categoryError })), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-4 items-center gap-4", children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "name", className: "text-right", children: "Name" }), (0, jsx_runtime_1.jsx)(input_1.Input, { id: "name", value: newCategoryName, onChange: function (e) { return setNewCategoryName(e.target.value); }, className: "col-span-3", placeholder: "e.g., Programming", disabled: isSavingCategory })] }), (0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-4 items-center gap-4", children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "description", className: "text-right", children: "Description" }), (0, jsx_runtime_1.jsx)(textarea_1.Textarea, { id: "description", value: newCategoryDescription, onChange: function (e) { return setNewCategoryDescription(e.target.value); }, className: "col-span-3", placeholder: "(Optional) A brief description", rows: 3, disabled: isSavingCategory })] })] }), (0, jsx_runtime_1.jsxs)(dialog_1.DialogFooter, { children: [(0, jsx_runtime_1.jsx)(dialog_1.DialogClose, { asChild: true, children: (0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", variant: "outline", disabled: isSavingCategory, children: "Cancel" }) }), (0, jsx_runtime_1.jsxs)(button_1.Button, { type: "button", onClick: handleAddCategory, disabled: isSavingCategory || !newCategoryName.trim(), children: [isSavingCategory ? (0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, "Save Category"] })] })] })] }), (0, jsx_runtime_1.jsx)(dialog_1.Dialog, { open: !!confirmDeleteCategoryId, onOpenChange: function () { return setConfirmDeleteCategoryId(null); }, children: (0, jsx_runtime_1.jsxs)(dialog_1.DialogContent, { className: "sm:max-w-[400px] z-50 bg-white", children: [(0, jsx_runtime_1.jsxs)(dialog_1.DialogHeader, { children: [(0, jsx_runtime_1.jsx)(dialog_1.DialogTitle, { children: "Confirm Deletion" }), (0, jsx_runtime_1.jsxs)(dialog_1.DialogDescription, { children: ["Are you sure you want to delete the category:", (0, jsx_runtime_1.jsx)("span", { className: "font-semibold mx-1", children: (_c = (_b = categories.find(function (c) { return c.id === confirmDeleteCategoryId; })) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : '' }), "? This action cannot be undone."] })] }), error && ((0, jsx_runtime_1.jsx)("p", { className: "text-sm text-red-600 bg-red-50 p-2 rounded border border-red-200 mt-2", children: error })), (0, jsx_runtime_1.jsxs)(dialog_1.DialogFooter, { className: "mt-4", children: [(0, jsx_runtime_1.jsx)(dialog_1.DialogClose, { asChild: true, children: (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "outline", disabled: isDeletingCategory, children: "Cancel" }) }), (0, jsx_runtime_1.jsxs)(button_1.Button, { variant: "destructive", onClick: handleConfirmDelete, disabled: isDeletingCategory, children: [isDeletingCategory ? (0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, "Delete"] })] })] }) })] }));
};
exports.CategoryManager = CategoryManager;
exports.default = exports.CategoryManager;