UNPKG

@defikitdotnet/education-module-ai

Version:
416 lines (415 loc) 23.4 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.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;