@defikitdotnet/education-module-ai
Version:
AI Education Module using Agent Framework
416 lines (415 loc) • 23.4 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.UserProvider = exports.useUser = void 0;
var jsx_runtime_1 = require("react/jsx-runtime");
var react_1 = require("react");
// Default context values
var defaultContextValue = {
isAuthenticated: false,
userId: null,
isVerifying: true, // Start verifying on load
// currentCourseAgentId: null, // Removed
userProfile: null,
// login: async () => false, // Removed
logout: function () { },
updateProfile: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, null];
}); }); },
updateLearningStyles: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, false];
}); }); },
addSubject: function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
return [2 /*return*/, false];
}); }); },
loading: true, // Initially loading (same as isVerifying)
error: null,
};
// Create context
var UserContext = (0, react_1.createContext)(defaultContextValue);
// Hook for using the user context
var useUser = function () { return (0, react_1.useContext)(UserContext); };
exports.useUser = useUser;
var UserProvider = function (_a) {
var children = _a.children, apiBaseUrl = _a.apiBaseUrl;
var _b = (0, react_1.useState)(false), isAuthenticated = _b[0], setIsAuthenticated = _b[1];
var _c = (0, react_1.useState)(null), userId = _c[0], setUserId = _c[1];
// --- Removed currentCourseAgentId state ---
// const [currentCourseAgentId, setCurrentCourseAgentId] = useState<
// string | null
// >(null);
var _d = (0, react_1.useState)(null), userProfile = _d[0], setUserProfile = _d[1];
var _e = (0, react_1.useState)(true), isVerifying = _e[0], setIsVerifying = _e[1]; // Tracks initial session verification
var _f = (0, react_1.useState)(false), operationLoading = _f[0], setOperationLoading = _f[1]; // Keep for specific ops like updates
var _g = (0, react_1.useState)(null), error = _g[0], setError = _g[1];
// Renamed fetchUserProfile to verifySessionAndFetchProfile for clarity
var verifySessionAndFetchProfile = (0, react_1.useCallback)(function () { return __awaiter(void 0, void 0, void 0, function () {
var response, statusResponse, statusData, data, err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
console.log("[UserContext DEBUG] verifySessionAndFetchProfile START");
setIsVerifying(true); // Explicitly set verifying true at the start
setError(null);
_a.label = 1;
case 1:
_a.trys.push([1, 9, 10, 11]);
// First try the /api/auth/student/me endpoint
console.log("[UserContext DEBUG] Fetching /api/auth/student/me...");
return [4 /*yield*/, fetch("".concat(apiBaseUrl, "/api/auth/student/me"), {
credentials: 'include', // Crucial: sends the httpOnly cookie
})];
case 2:
response = _a.sent();
console.log("[UserContext DEBUG] Fetch response status:", response.status);
if (!(!response.ok && response.status === 404)) return [3 /*break*/, 5];
console.log("[UserContext DEBUG] /me endpoint not found, trying /status fallback...");
return [4 /*yield*/, fetch("".concat(apiBaseUrl, "/api/auth/student/status"), {
credentials: 'include',
})];
case 3:
statusResponse = _a.sent();
if (!statusResponse.ok) {
if (statusResponse.status === 401 || statusResponse.status === 404) {
console.warn("[UserContext DEBUG] Received ".concat(statusResponse.status, " on status fetch. Setting logged out state."));
setIsAuthenticated(false);
setUserId(null);
setUserProfile(null);
return [2 /*return*/];
}
else {
throw new Error("Failed to fetch profile: ".concat(statusResponse.statusText));
}
}
return [4 /*yield*/, statusResponse.json()];
case 4:
statusData = _a.sent();
// Handle status endpoint response format
if (statusData.isAuthenticated && statusData.student) {
setUserProfile(statusData.student);
setUserId(statusData.student.id);
setIsAuthenticated(true);
console.log("[UserContext DEBUG] Profile fetched from /status endpoint successfully, isAuthenticated = true. User ID:", statusData.student.id);
return [2 /*return*/];
}
_a.label = 5;
case 5:
if (!!response.ok) return [3 /*break*/, 6];
// If API says unauthorized (401) or not found (404), treat as logged out
if (response.status === 401 || response.status === 404) {
console.warn("[UserContext DEBUG] Received ".concat(response.status, " on profile fetch. Setting logged out state."));
// No need to call logout() function here, just set state directly
setIsAuthenticated(false);
setUserId(null);
setUserProfile(null);
}
else {
// For other errors, just set error state
throw new Error("Failed to fetch profile: ".concat(response.statusText));
}
return [3 /*break*/, 8];
case 6: return [4 /*yield*/, response.json()];
case 7:
data = _a.sent();
console.log("[UserContext DEBUG] Raw API response:", data);
// Handle multiple possible response formats
if (data.data && data.data.id) {
// Format from the /me endpoint we just created
setUserProfile(data.data);
setUserId(data.data.id);
setIsAuthenticated(true);
console.log("[UserContext DEBUG] Profile fetched successfully from /me endpoint, isAuthenticated = true. User ID:", data.data.id);
}
else if (data.isAuthenticated && data.student && data.student.id) {
// Format from the /status endpoint
setUserProfile(data.student);
setUserId(data.student.id);
setIsAuthenticated(true);
console.log("[UserContext DEBUG] Profile fetched successfully from old format, isAuthenticated = true. User ID:", data.student.id);
}
else {
console.warn("[UserContext DEBUG] API response has unexpected format. Treating as logged out.", data);
setIsAuthenticated(false);
setUserId(null);
setUserProfile(null);
}
_a.label = 8;
case 8: return [3 /*break*/, 11];
case 9:
err_1 = _a.sent();
console.error("[UserContext DEBUG] Error during verifySessionAndFetchProfile:", err_1);
setError(err_1 instanceof Error ? err_1.message : "Failed to verify session");
// Ensure state reflects logged-out status on error
setIsAuthenticated(false);
setUserId(null);
setUserProfile(null);
return [3 /*break*/, 11];
case 10:
setIsVerifying(false); // Verification attempt finished
console.log("[UserContext DEBUG] verifySessionAndFetchProfile finished. isVerifying=false, isAuthenticated=", isAuthenticated);
return [7 /*endfinally*/];
case 11: return [2 /*return*/];
}
});
}); }, [apiBaseUrl]); // Let's try with just apiBaseUrl
// useEffect runs on mount to check the session status via API
(0, react_1.useEffect)(function () {
console.log("[UserContext DEBUG] useEffect Mount - Calling verifySessionAndFetchProfile");
verifySessionAndFetchProfile();
// Add verifySessionAndFetchProfile to dependency array
}, [verifySessionAndFetchProfile]); // Dependency ensures it runs when the callback is defined
// --- Removed login function ---
// Logout function - simply clears React state. Assumes backend handles cookie deletion via endpoint or expiry.
var logout = (0, react_1.useCallback)(function () {
console.log("[UserContext DEBUG] logout called (clearing state).");
// Call the backend logout endpoint
fetch("".concat(apiBaseUrl, "/api/auth/student/logout"), {
method: 'POST',
credentials: 'include'
})
.then(function (res) { return console.log('Backend logout response:', res.status); })
.catch(function (err) { return console.error('Backend logout failed:', err); });
// Clear state
setUserId(null);
setIsAuthenticated(false);
setUserProfile(null);
setError(null);
setIsVerifying(false); // No longer verifying after logout
}, [apiBaseUrl]); // Added apiBaseUrl dependency
// Update profile for the authenticated user - Now uses userId from state
var updateProfile = function (profile) { return __awaiter(void 0, void 0, void 0, function () {
var response, data, err_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
// Check authentication status based on state
if (!isAuthenticated || isVerifying || !userId) {
console.warn("[UserContext DEBUG] updateProfile aborted. Auth/Verify/ID state:", { isAuthenticated: isAuthenticated, isVerifying: isVerifying, userId: userId });
return [2 /*return*/, null];
}
setOperationLoading(true);
setError(null);
_a.label = 1;
case 1:
_a.trys.push([1, 4, 5, 6]);
// Endpoint is /api/auth/student/me (PUT)
console.log("[UserContext DEBUG] updateProfile - Sending PUT to /api/auth/student/me");
return [4 /*yield*/, fetch("".concat(apiBaseUrl, "/api/auth/student/me"), {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(profile),
credentials: 'include', // Include cookies
})];
case 2:
response = _a.sent();
console.log("[UserContext DEBUG] updateProfile - Response status:", response.status);
if (!response.ok) {
// Handle potential session expiry during update
if (response.status === 401) {
logout(); // Log out if session became invalid
}
throw new Error("Failed to update profile: ".concat(response.statusText));
}
return [4 /*yield*/, response.json()];
case 3:
data = _a.sent();
// Update local profile state with the response from backend
if (data.data && data.data.id === userId) { // Verify ID match
setUserProfile(data.data);
console.log("[UserContext DEBUG] updateProfile - Success. Updated profile:", data.data);
return [2 /*return*/, data.data];
}
else {
console.warn("[UserContext DEBUG] updateProfile - Response ID mismatch or missing data. Logging out.", data);
logout(); // Logout on mismatch
return [2 /*return*/, null];
}
return [3 /*break*/, 6];
case 4:
err_2 = _a.sent();
console.error("[UserContext DEBUG] Error updating profile:", err_2);
setError(err_2 instanceof Error ? err_2.message : "Failed to update profile");
return [2 /*return*/, null];
case 5:
setOperationLoading(false);
return [7 /*endfinally*/];
case 6: return [2 /*return*/];
}
});
}); };
// Update learning styles - Uses userId from state
var updateLearningStyles = function (styles) { return __awaiter(void 0, void 0, void 0, function () {
var response, err_3;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!userId || !isAuthenticated || isVerifying) {
console.warn("[UserContext DEBUG] updateLearningStyles aborted. Auth/Verify/ID state:", { isAuthenticated: isAuthenticated, isVerifying: isVerifying, userId: userId });
return [2 /*return*/, false];
}
setOperationLoading(true);
setError(null);
console.log("[UserContext DEBUG] updateLearningStyles for user ".concat(userId));
_a.label = 1;
case 1:
_a.trys.push([1, 4, 5, 6]);
return [4 /*yield*/, fetch(
// --- Updated endpoint assumption ---
"".concat(apiBaseUrl, "/api/auth/student/me/learning-styles"), {
method: "PUT", // Or POST? Assuming PUT to replace
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ learningStyles: styles }),
credentials: 'include',
})];
case 2:
response = _a.sent();
console.log("[UserContext DEBUG] updateLearningStyles - Response status:", response.status);
if (!response.ok) {
if (response.status === 401)
logout();
throw new Error("Failed to update learning styles: ".concat(response.statusText));
}
// Assuming the response might contain the updated profile or just a success message
// Let's refetch the profile to ensure consistency, or update based on response if available
return [4 /*yield*/, verifySessionAndFetchProfile()];
case 3:
// Assuming the response might contain the updated profile or just a success message
// Let's refetch the profile to ensure consistency, or update based on response if available
_a.sent(); // Refetch profile after update
return [2 /*return*/, true];
case 4:
err_3 = _a.sent();
console.error("[UserContext DEBUG] Error updating learning styles:", err_3);
setError(err_3 instanceof Error ? err_3.message : "Failed to update learning styles");
return [2 /*return*/, false];
case 5:
setOperationLoading(false);
return [7 /*endfinally*/];
case 6: return [2 /*return*/];
}
});
}); };
// Add subject - Uses userId from state
var addSubject = function (subject_1) {
var args_1 = [];
for (var _i = 1; _i < arguments.length; _i++) {
args_1[_i - 1] = arguments[_i];
}
return __awaiter(void 0, __spreadArray([subject_1], args_1, true), void 0, function (subject, level) {
var response, err_4;
if (level === void 0) { level = 1; }
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!userId || !isAuthenticated || isVerifying) {
console.warn("[UserContext DEBUG] addSubject aborted. Auth/Verify/ID state:", { isAuthenticated: isAuthenticated, isVerifying: isVerifying, userId: userId });
return [2 /*return*/, false];
}
setOperationLoading(true);
setError(null);
console.log("[UserContext DEBUG] addSubject for user ".concat(userId));
_a.label = 1;
case 1:
_a.trys.push([1, 4, 5, 6]);
return [4 /*yield*/, fetch(
// --- Updated endpoint assumption ---
"".concat(apiBaseUrl, "/api/auth/student/me/subjects"), {
method: "POST", // POST to add a new one
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ subject: subject, level: level }),
credentials: 'include',
})];
case 2:
response = _a.sent();
console.log("[UserContext DEBUG] addSubject - Response status:", response.status);
if (!response.ok) {
if (response.status === 401)
logout();
throw new Error("Failed to add subject: ".concat(response.statusText));
}
// Refetch profile after adding subject
return [4 /*yield*/, verifySessionAndFetchProfile()];
case 3:
// Refetch profile after adding subject
_a.sent();
return [2 /*return*/, true];
case 4:
err_4 = _a.sent();
console.error("[UserContext DEBUG] Error adding subject:", err_4);
setError(err_4 instanceof Error ? err_4.message : "Failed to add subject");
return [2 /*return*/, false];
case 5:
setOperationLoading(false);
return [7 /*endfinally*/];
case 6: return [2 /*return*/];
}
});
});
};
return ((0, jsx_runtime_1.jsx)(UserContext.Provider, { value: {
isAuthenticated: isAuthenticated,
userId: userId,
isVerifying: isVerifying,
// currentCourseAgentId, // Removed
userProfile: userProfile,
// login, // Removed
logout: logout,
updateProfile: updateProfile,
updateLearningStyles: updateLearningStyles,
addSubject: addSubject,
// Combine loading states: isVerifying for initial load, operationLoading for subsequent actions
loading: isVerifying || operationLoading,
error: error,
}, children: children }));
};
exports.UserProvider = UserProvider;