UNPKG

@defikitdotnet/education-module-ai

Version:
283 lines (282 loc) 21.1 kB
"use strict"; 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;