UNPKG

@defikitdotnet/education-module-ai

Version:
552 lines (551 loc) 44.4 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; 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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var jsx_runtime_1 = require("react/jsx-runtime"); var react_1 = require("react"); var router_1 = require("next/router"); var link_1 = __importDefault(require("next/link")); var axios_1 = __importDefault(require("axios")); var sonner_1 = require("sonner"); // Using sonner for toasts // Shadcn UI Components for Auth and Layout var button_1 = require("../../components/ui/button"); var card_1 = require("../../components/ui/card"); var input_1 = require("../../components/ui/input"); var label_1 = require("../../components/ui/label"); var tabs_1 = require("../../components/ui/tabs"); // Define level name mapping for UI display var levelNames = { beginner: "Beginner", intermediate: "Intermediate", advanced: "Advanced", }; var StudentPortalPage = function () { var router = (0, router_1.useRouter)(); var _a = (0, react_1.useState)(null), agentId = _a[0], setAgentId = _a[1]; // Auth State var _b = (0, react_1.useState)(false), isAuthenticated = _b[0], setIsAuthenticated = _b[1]; var _c = (0, react_1.useState)(null), student = _c[0], setStudent = _c[1]; var _d = (0, react_1.useState)(true), isAuthLoading = _d[0], setIsAuthLoading = _d[1]; var _e = (0, react_1.useState)(null), authError = _e[0], setAuthError = _e[1]; // Course State var _f = (0, react_1.useState)([]), courses = _f[0], setCourses = _f[1]; var _g = (0, react_1.useState)(false), isCourseLoading = _g[0], setIsCourseLoading = _g[1]; var _h = (0, react_1.useState)(null), courseError = _h[0], setCourseError = _h[1]; var _j = (0, react_1.useState)("Teacher"), teacherName = _j[0], setTeacherName = _j[1]; // Placeholder // Category State var _k = (0, react_1.useState)([]), categories = _k[0], setCategories = _k[1]; var _l = (0, react_1.useState)(false), isCategoryLoading = _l[0], setIsCategoryLoading = _l[1]; var _m = (0, react_1.useState)(null), categoryError = _m[0], setCategoryError = _m[1]; // Form State (Login/Signup) var _o = (0, react_1.useState)(""), loginEmail = _o[0], setLoginEmail = _o[1]; var _p = (0, react_1.useState)(""), loginPassword = _p[0], setLoginPassword = _p[1]; var _q = (0, react_1.useState)(""), signupName = _q[0], setSignupName = _q[1]; var _r = (0, react_1.useState)(""), signupEmail = _r[0], setSignupEmail = _r[1]; var _s = (0, react_1.useState)(""), signupPassword = _s[0], setSignupPassword = _s[1]; var _t = (0, react_1.useState)(false), isSubmitting = _t[0], setIsSubmitting = _t[1]; // Add state for controlling the active auth tab var _u = (0, react_1.useState)("login"), activeAuthTab = _u[0], setActiveAuthTab = _u[1]; // Add this new state for enrollments var _v = (0, react_1.useState)([]), enrollments = _v[0], setEnrollments = _v[1]; var _w = (0, react_1.useState)(false), isEnrollmentLoading = _w[0], setIsEnrollmentLoading = _w[1]; // --- Authentication --- var checkAuthStatus = (0, react_1.useCallback)(function () { return __awaiter(void 0, void 0, void 0, function () { var response, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: setIsAuthLoading(true); setAuthError(null); _a.label = 1; case 1: _a.trys.push([1, 3, 4, 5]); return [4 /*yield*/, axios_1.default.get("".concat(process.env.NEXT_PUBLIC_BACKEND_URL, "/api/auth/student/status"), { withCredentials: true })]; case 2: response = _a.sent(); if (response.data.isAuthenticated) { setIsAuthenticated(true); setStudent(response.data.student); } else { setIsAuthenticated(false); setStudent(null); } return [3 /*break*/, 5]; case 3: error_1 = _a.sent(); console.error("Auth status check failed:", error_1); setIsAuthenticated(false); setStudent(null); return [3 /*break*/, 5]; case 4: setIsAuthLoading(false); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }, []); (0, react_1.useEffect)(function () { // Extract teacherId from URL query if (router.isReady) { var queryTeacherId = router.query.teacherId; if (typeof queryTeacherId === "string") { setAgentId(queryTeacherId); // Fetch teacher details based on teacherId if needed (optional) // fetchTeacherDetails(queryTeacherId); } else { // Only set error if teacherId is explicitly missing after router is ready setAuthError("Teacher ID not found in URL. Please use the link provided by your teacher."); setIsAuthLoading(false); // Stop loading if URL is invalid } } }, [router.isReady, router.query]); (0, react_1.useEffect)(function () { // Check auth status once teacherId is confirmed if (agentId) { checkAuthStatus(); } // If no teacherId after router ready, don't proceed with auth check else if (router.isReady && !agentId) { setIsAuthLoading(false); // Ensure loading stops } }, [agentId, checkAuthStatus, router.isReady]); // --- Course Fetching (runs when authenticated and agentId is set) --- var fetchEnrollments = (0, react_1.useCallback)(function () { return __awaiter(void 0, void 0, void 0, function () { var apiUrl, response, error_2; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!isAuthenticated || !agentId) return [2 /*return*/]; setIsEnrollmentLoading(true); _a.label = 1; case 1: _a.trys.push([1, 3, 4, 5]); apiUrl = process.env.NEXT_PUBLIC_BACKEND_URL || ""; return [4 /*yield*/, axios_1.default.get("".concat(apiUrl, "/api/enrollments/student"), { withCredentials: true })]; case 2: response = _a.sent(); console.log("Enrollments response:", response.data); setEnrollments(response.data || []); return [3 /*break*/, 5]; case 3: error_2 = _a.sent(); console.error("Error fetching enrollments:", error_2); // We don't need to show error for enrollments setEnrollments([]); return [3 /*break*/, 5]; case 4: setIsEnrollmentLoading(false); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }, [isAuthenticated, agentId]); (0, react_1.useEffect)(function () { if (isAuthenticated && agentId) { fetchEnrollments(); } }, [isAuthenticated, agentId, fetchEnrollments]); var findEnrollmentForCourse = function (courseId) { return enrollments.find(function (enrollment) { return enrollment.courseId === courseId; }); }; var fetchCoursesForAgent = (0, react_1.useCallback)(function () { return __awaiter(void 0, void 0, void 0, function () { var apiUrl, response, fetchedCourses, coursesWithEnrollment, error_3; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!isAuthenticated || !agentId) return [2 /*return*/]; setIsCourseLoading(true); setCourseError(null); _c.label = 1; case 1: _c.trys.push([1, 3, 4, 5]); apiUrl = process.env.NEXT_PUBLIC_BACKEND_URL || ""; return [4 /*yield*/, axios_1.default.get("".concat(apiUrl, "/api/courses/teacher/").concat(agentId), { withCredentials: true, // Important for sessions/cookies if used })]; case 2: response = _c.sent(); // Add debugging for course data console.log("Courses response:", response.data); fetchedCourses = []; if (Array.isArray(response.data)) { // If response.data is already an array fetchedCourses = response.data; } else if (response.data && Array.isArray(response.data.courses)) { // If response.data is an object with a courses property that's an array fetchedCourses = response.data.courses; } else if (response.data && typeof response.data === 'object') { // If response.data is some other object, log a warning and try to handle it console.warn("Unexpected courses data structure:", response.data); fetchedCourses = []; // Default to empty array } coursesWithEnrollment = fetchedCourses.map(function (course) { var enrollment = findEnrollmentForCourse(course.id); return __assign(__assign({}, course), { enrollment: enrollment }); }); console.log("Setting courses with enrollment:", coursesWithEnrollment); setCourses(coursesWithEnrollment); // Optionally fetch teacher name here if not included in course data if (fetchedCourses && fetchedCourses.length > 0) { // You might need another API endpoint to get teacher details // const teacherDetails = await axios.get(`${apiUrl}/api/teachers/${agentId}`); // setTeacherName(teacherDetails.data.name || "Teacher"); setTeacherName("Teacher"); // Default fallback } return [3 /*break*/, 5]; case 3: error_3 = _c.sent(); console.error("Error fetching courses for teacher ".concat(agentId, ":"), error_3); setCourseError(((_b = (_a = error_3.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) || "Failed to load courses for this teacher."); setCourses([]); return [3 /*break*/, 5]; case 4: setIsCourseLoading(false); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }, [isAuthenticated, agentId, enrollments]); // --- Category Fetching (runs when agentId is set) --- var fetchCategories = (0, react_1.useCallback)(function () { return __awaiter(void 0, void 0, void 0, function () { var apiUrl, response, fetchedCategories, error_4; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: if (!agentId) return [2 /*return*/]; // Need agentId to fetch relevant categories setIsCategoryLoading(true); setCategoryError(null); _c.label = 1; case 1: _c.trys.push([1, 3, 4, 5]); apiUrl = process.env.NEXT_PUBLIC_BACKEND_URL || ""; return [4 /*yield*/, axios_1.default.get("".concat(apiUrl, "/api/categories/teacher/").concat(agentId))]; case 2: response = _c.sent(); // Add debugging to check the response structure console.log("Categories response:", response.data); fetchedCategories = response.data || []; setCategories(fetchedCategories); // Log the categories being set console.log("Categories set:", fetchedCategories); return [3 /*break*/, 5]; case 3: error_4 = _c.sent(); console.error("Error fetching categories for agent ".concat(agentId, ":"), error_4); setCategoryError(((_b = (_a = error_4.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) || "Failed to load course categories."); setCategories([]); return [3 /*break*/, 5]; case 4: setIsCategoryLoading(false); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }, [agentId]); (0, react_1.useEffect)(function () { // Fetch courses only when authenticated and agentId is available if (isAuthenticated && agentId) { fetchCoursesForAgent(); } }, [isAuthenticated, agentId, fetchCoursesForAgent]); (0, react_1.useEffect)(function () { // Fetch categories when agentId is available (independent of auth) if (agentId) { fetchCategories(); } }, [agentId, fetchCategories]); // --- Auth Form Handlers --- var handleLogin = function (event) { return __awaiter(void 0, void 0, void 0, function () { var apiUrl, response, error_5; var _a, _b, _c; return __generator(this, function (_d) { switch (_d.label) { case 0: event.preventDefault(); if (!loginEmail || !loginPassword) { sonner_1.toast.error("Please enter both email and password."); return [2 /*return*/]; } setIsSubmitting(true); _d.label = 1; case 1: _d.trys.push([1, 3, 4, 5]); apiUrl = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:3009"; return [4 /*yield*/, axios_1.default.post("".concat(apiUrl, "/api/student/login"), { email: loginEmail, password: loginPassword, }, { withCredentials: true })]; case 2: response = _d.sent(); // Check if the backend reported success explicitly if (response.data && response.data.message === "Login successful") { sonner_1.toast.success("Login successful!"); // Remove the immediate checkAuthStatus() call // await checkAuthStatus(); // Just reload the page. The useEffect hooks will run checkAuthStatus on reload. router.reload(); } else { // Handle cases where backend might return 200 but indicate failure throw new Error(((_a = response.data) === null || _a === void 0 ? void 0 : _a.message) || "Login failed: Invalid response from server."); } return [3 /*break*/, 5]; case 3: error_5 = _d.sent(); console.error("Login failed:", error_5); sonner_1.toast.error(((_c = (_b = error_5.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) || "Login failed. Please check your credentials."); return [3 /*break*/, 5]; case 4: setIsSubmitting(false); setLoginPassword(""); // Clear password field return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }; var handleSignup = function (event) { return __awaiter(void 0, void 0, void 0, function () { var response, error_6; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: event.preventDefault(); if (!signupName || !signupEmail || !signupPassword) { sonner_1.toast.error("Please fill in all signup fields."); return [2 /*return*/]; } setIsSubmitting(true); _c.label = 1; case 1: _c.trys.push([1, 3, 4, 5]); return [4 /*yield*/, axios_1.default.post("".concat(process.env.NEXT_PUBLIC_BACKEND_URL, "/api/student/register"), { name: signupName, email: signupEmail, password: signupPassword, })]; case 2: response = _c.sent(); sonner_1.toast.success(response.data.message || "Signup successful! Please log in."); // Optionally switch to login tab or clear form setSignupName(""); setSignupEmail(""); setSignupPassword(""); setActiveAuthTab("login"); // Switch to login tab after successful signup return [3 /*break*/, 5]; case 3: error_6 = _c.sent(); console.error("Signup failed:", error_6); sonner_1.toast.error(((_b = (_a = error_6.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.message) || "Signup failed. Please try again."); return [3 /*break*/, 5]; case 4: setIsSubmitting(false); return [7 /*endfinally*/]; case 5: return [2 /*return*/]; } }); }); }; var handleLogout = function () { return __awaiter(void 0, void 0, void 0, function () { var apiUrl, response, error_7; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); console.log("Attempting to log out student..."); // First clear the localStorage for agentId when using it localStorage.removeItem('currentAgentId'); apiUrl = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:3009"; return [4 /*yield*/, axios_1.default.post("".concat(apiUrl, "/api/auth/student/logout"), {}, { withCredentials: true // Important for cookie-based auth })]; case 1: response = _a.sent(); // Log the response console.log("Logout response:", response); // Clear local state setIsAuthenticated(false); setStudent(null); setCourses([]); // Clear courses on logout setCategories([]); // Clear categories on logout // Clear form fields on logout setLoginEmail(""); setLoginPassword(""); setSignupName(""); setSignupEmail(""); setSignupPassword(""); setActiveAuthTab("login"); // Reset tab return [3 /*break*/, 3]; case 2: error_7 = _a.sent(); console.error("Logout failed:", error_7); sonner_1.toast.error("Logout failed. Please try again."); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); }; // --- Helper function to parse categories safely --- var parseCourseCategories = function (categoriesJson) { if (!categoriesJson) return []; try { console.log("Parsing categories JSON:", categoriesJson); // Handle different possible formats if (typeof categoriesJson === 'string') { // If it's a JSON string, try to parse it try { var parsed = JSON.parse(categoriesJson); console.log("Parsed categories:", parsed); // Handle array of strings or array of objects with id property if (Array.isArray(parsed)) { return parsed.map(function (item) { return typeof item === 'object' && item !== null ? item.id || item : item; }); } // Handle single string or object else if (parsed) { return [typeof parsed === 'object' ? parsed.id || '' : parsed]; } } catch (jsonError) { // If JSON parsing fails, it might be a comma-separated string if (categoriesJson.includes(',')) { return categoriesJson.split(',').map(function (id) { return id.trim(); }); } // Or a single category ID return [categoriesJson]; } } // If categories is already an array (not a string that needs parsing) else if (Array.isArray(categoriesJson)) { return categoriesJson.map(function (item) { return typeof item === 'object' && item !== null ? item.id || item : item; }); } return []; } catch (error) { console.error("Failed to parse course categories JSON:", error); return []; } }; // --- Rendering Logic --- // Combined Loading State for initial page load if (isAuthLoading || (!isAuthenticated && !authError && agentId === null)) { // Show loading until agentId is determined or auth check completes return ((0, jsx_runtime_1.jsxs)("div", { className: "min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-gray-600 dark:text-gray-400 text-lg", children: "Loading Access..." }), (0, jsx_runtime_1.jsx)("div", { className: "ml-4 w-5 h-5 border-t-2 border-blue-500 border-solid rounded-full animate-spin" })] })); } // Error State if Agent ID is missing or invalid if (authError) { // Display error if authError is set (e.g., invalid agentId) return ((0, jsx_runtime_1.jsx)("div", { className: "min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 p-4", children: (0, jsx_runtime_1.jsxs)(card_1.Card, { className: "w-full max-w-md bg-red-50 dark:bg-red-900/20 border-red-300 dark:border-red-700", children: [(0, jsx_runtime_1.jsx)(card_1.CardHeader, { children: (0, jsx_runtime_1.jsx)(card_1.CardTitle, { className: "text-red-700 dark:text-red-400", children: "Access Error" }) }), (0, jsx_runtime_1.jsxs)(card_1.CardContent, { children: [(0, jsx_runtime_1.jsx)("p", { className: "text-red-600 dark:text-red-400", children: authError }), (0, jsx_runtime_1.jsx)("p", { className: "mt-4 text-sm text-gray-600 dark:text-gray-400", children: "Please check the URL or contact the teacher who shared it." })] })] }) })); } // --- Render Auth Forms if not authenticated --- if (!isAuthenticated) { return ((0, jsx_runtime_1.jsxs)("div", { className: "min-h-screen flex items-center justify-center bg-gray-100 dark:bg-gray-900 p-4", children: [(0, jsx_runtime_1.jsx)(sonner_1.Toaster, { position: "top-center", richColors: true }), (0, jsx_runtime_1.jsxs)(tabs_1.Tabs, { value: activeAuthTab, onValueChange: setActiveAuthTab, className: "w-full max-w-md", children: [(0, jsx_runtime_1.jsxs)(tabs_1.TabsList, { className: "grid w-full grid-cols-2", children: [(0, jsx_runtime_1.jsx)(tabs_1.TabsTrigger, { value: "login", children: "Login" }), (0, jsx_runtime_1.jsx)(tabs_1.TabsTrigger, { value: "signup", children: "Sign Up" })] }), (0, jsx_runtime_1.jsx)(tabs_1.TabsContent, { value: "login", children: (0, jsx_runtime_1.jsxs)(card_1.Card, { children: [(0, jsx_runtime_1.jsxs)(card_1.CardHeader, { children: [(0, jsx_runtime_1.jsx)(card_1.CardTitle, { children: "Student Login" }), (0, jsx_runtime_1.jsx)(card_1.CardDescription, { children: "Access your courses by logging in." })] }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleLogin, children: [(0, jsx_runtime_1.jsxs)(card_1.CardContent, { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "login-email", children: "Email" }), (0, jsx_runtime_1.jsx)(input_1.Input, { id: "login-email", type: "email", placeholder: "m@example.com", required: true, value: loginEmail, onChange: function (e) { return setLoginEmail(e.target.value); }, disabled: isSubmitting })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "login-password", children: "Password" }), (0, jsx_runtime_1.jsx)(input_1.Input, { id: "login-password", type: "password", required: true, value: loginPassword, onChange: function (e) { return setLoginPassword(e.target.value); }, disabled: isSubmitting })] })] }), (0, jsx_runtime_1.jsx)(card_1.CardFooter, { children: (0, jsx_runtime_1.jsx)(button_1.Button, { type: "submit", className: "w-full", disabled: isSubmitting, children: isSubmitting ? "Logging In..." : "Login" }) })] })] }) }), (0, jsx_runtime_1.jsx)(tabs_1.TabsContent, { value: "signup", children: (0, jsx_runtime_1.jsxs)(card_1.Card, { children: [(0, jsx_runtime_1.jsxs)(card_1.CardHeader, { children: [(0, jsx_runtime_1.jsx)(card_1.CardTitle, { children: "Student Sign Up" }), (0, jsx_runtime_1.jsx)(card_1.CardDescription, { children: "Create an account to enroll in courses." })] }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSignup, children: [(0, jsx_runtime_1.jsxs)(card_1.CardContent, { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "signup-name", children: "Name" }), (0, jsx_runtime_1.jsx)(input_1.Input, { id: "signup-name", placeholder: "Your Name", required: true, value: signupName, onChange: function (e) { return setSignupName(e.target.value); }, disabled: isSubmitting })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "signup-email", children: "Email" }), (0, jsx_runtime_1.jsx)(input_1.Input, { id: "signup-email", type: "email", placeholder: "m@example.com", required: true, value: signupEmail, onChange: function (e) { return setSignupEmail(e.target.value); }, disabled: isSubmitting })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)(label_1.Label, { htmlFor: "signup-password", children: "Password" }), (0, jsx_runtime_1.jsx)(input_1.Input, { id: "signup-password", type: "password", required: true, value: signupPassword, onChange: function (e) { return setSignupPassword(e.target.value); }, disabled: isSubmitting })] })] }), (0, jsx_runtime_1.jsx)(card_1.CardFooter, { children: (0, jsx_runtime_1.jsx)(button_1.Button, { type: "submit", className: "w-full", disabled: isSubmitting, children: isSubmitting ? "Signing Up..." : "Sign Up" }) })] })] }) })] })] })); } // --- Render Authenticated Student Dashboard --- return ((0, jsx_runtime_1.jsxs)("div", { className: "min-h-screen bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100", children: [(0, jsx_runtime_1.jsx)(sonner_1.Toaster, { position: "top-center", richColors: true }), (0, jsx_runtime_1.jsx)("header", { className: "bg-white dark:bg-gray-800 shadow-sm", children: (0, jsx_runtime_1.jsx)("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between items-center h-16", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex-shrink-0 flex items-center", children: (0, jsx_runtime_1.jsxs)("span", { className: "font-bold text-xl", children: [teacherName, "'s Courses"] }) }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center space-x-4", children: [(0, jsx_runtime_1.jsxs)("span", { className: "text-sm font-medium", children: ["Welcome, ", (student === null || student === void 0 ? void 0 : student.name) || "Student", "!"] }), (0, jsx_runtime_1.jsx)(link_1.default, { href: "/student/profile?teacherId=".concat(agentId), passHref: true, children: (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "outline", size: "sm", children: "Profile" }) }), (0, jsx_runtime_1.jsx)(button_1.Button, { variant: "ghost", size: "sm", onClick: handleLogout, children: "Logout" })] })] }) }) }), (0, jsx_runtime_1.jsxs)("main", { className: "max-w-7xl mx-auto py-6 sm:px-6 lg:px-8", children: [(isCourseLoading || isCategoryLoading) && ((0, jsx_runtime_1.jsxs)("div", { className: "text-center py-16", children: [(0, jsx_runtime_1.jsx)("div", { className: "inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]" }), (0, jsx_runtime_1.jsx)("p", { className: "mt-4 text-gray-500 dark:text-gray-400", children: "Loading your learning journey..." })] })), !isCourseLoading && !isCategoryLoading && (courseError || categoryError) && ((0, jsx_runtime_1.jsx)("div", { className: "px-4 py-6 sm:px-0", children: (0, jsx_runtime_1.jsxs)(card_1.Card, { className: "bg-yellow-50 dark:bg-yellow-900/20 border-yellow-300 dark:border-yellow-700", children: [(0, jsx_runtime_1.jsx)(card_1.CardHeader, { children: (0, jsx_runtime_1.jsx)(card_1.CardTitle, { className: "text-yellow-700 dark:text-yellow-400", children: "Warning" }) }), (0, jsx_runtime_1.jsx)(card_1.CardContent, { children: (0, jsx_runtime_1.jsx)("p", { className: "text-yellow-600 dark:text-yellow-400", children: courseError || categoryError || "Could not load all course information." }) })] }) })), !isCourseLoading && !isCategoryLoading && !courseError && !categoryError && ((0, jsx_runtime_1.jsxs)("div", { className: "px-4 py-6 sm:px-0", children: [categories.length > 0 && ((0, jsx_runtime_1.jsx)("div", { className: "space-y-12", children: categories.map(function (category) { // Filter courses that belong to this category var coursesInCategory = courses.filter(function (course) { var courseCats = parseCourseCategories(course.categories); console.log("Course ".concat(course.title, " has categories:"), courseCats); return courseCats.some(function (catId) { return catId === category.id || // Also check if the category name matches in case IDs don't match exactly (typeof catId === 'string' && category.name && catId.toLowerCase() === category.name.toLowerCase()); }); }); // Only render categories that have courses if (coursesInCategory.length === 0) return null; return ((0, jsx_runtime_1.jsxs)("section", { children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-6", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-2xl font-bold tracking-tight", children: category.name }), category.description && ((0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-400", children: category.description }))] }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6", children: coursesInCategory.map(function (course) { return ((0, jsx_runtime_1.jsxs)(card_1.Card, { className: "overflow-hidden hover:shadow-lg transition-shadow duration-300 h-full flex flex-col", children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative pb-[56.25%] bg-gray-100 dark:bg-gray-800 overflow-hidden", children: [course.thumbnail ? ((0, jsx_runtime_1.jsx)("img", { src: course.thumbnail, alt: "".concat(course.title, " thumbnail"), className: "absolute inset-0 w-full h-full object-cover", onError: function (e) { e.target.src = "/placeholder-course.jpg"; } })) : ((0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 flex items-center justify-center bg-gray-200 dark:bg-gray-700", children: (0, jsx_runtime_1.jsx)("span", { className: "text-gray-400 dark:text-gray-500 text-sm", children: "No image" }) })), (0, jsx_runtime_1.jsx)("div", { className: "absolute top-2 left-2 bg-blue-600 text-white text-xs px-2 py-1 rounded", children: levelNames[course.level] || course.level })] }), (0, jsx_runtime_1.jsx)(card_1.CardHeader, { className: "p-4 pb-0", children: (0, jsx_runtime_1.jsx)(card_1.CardTitle, { className: "text-lg", children: course.title }) }), (0, jsx_runtime_1.jsxs)(card_1.CardContent, { className: "p-4 pt-2 flex-grow", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 dark:text-gray-400 line-clamp-3", children: course.description || "No description available." }), course.enrollment && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-xs mb-1", children: [(0, jsx_runtime_1.jsx)("span", { children: "Progress" }), (0, jsx_runtime_1.jsxs)("span", { children: [Math.round(course.enrollment.progress), "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2", children: (0, jsx_runtime_1.jsx)("div", { className: "bg-blue-600 h-2 rounded-full", style: { width: "".concat(course.enrollment.progress, "%") } }) })] }))] }), (0, jsx_runtime_1.jsx)(card_1.CardFooter, { className: "px-4 pb-4 pt-0 mt-auto", children: (0, jsx_runtime_1.jsx)(button_1.Button, { className: "w-full", onClick: function () { return router.push("/student/course/".concat(course.id, "?teacherId=").concat(agentId)); }, children: course.enrollment ? 'Continue Learning' : 'View Course' }) })] }, course.id)); }) })] }, category.id)); }) })), (function () { // Find courses that don't belong to any of the displayed categories var uncategorizedCourses = courses.filter(function (course) { var courseCats = parseCourseCategories(course.categories); // A course is considered uncategorized if: // 1. It has no categories at all, OR // 2. None of its categories match any category in our categories list if (courseCats.length === 0) return true; // Check if any of this course's categories match any category we have return !categories.some(function (category) { return courseCats.some(function (catId) { return catId === category.id || (typeof catId === 'string' && category.name && catId.toLowerCase() === category.name.toLowerCase()); }); }); }); if (uncategorizedCourses.length === 0) return null; return ((0, jsx_runtime_1.jsxs)("section", { className: "mt-12", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-6", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-2xl font-bold tracking-tight", children: "Other Courses" }), (0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-400", children: "Courses that don't belong to the displayed categories" })] }), (0, jsx_runtime_1.jsx)("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6", children: uncategorizedCourses.map(function (course) { return ((0, jsx_runtime_1.jsxs)(card_1.Card, { className: "overflow-hidden hover:shadow-lg transition-shadow duration-300 h-full flex flex-col", children: [(0, jsx_runtime_1.jsxs)("div", { className: "relative pb-[56.25%] bg-gray-100 dark:bg-gray-800 overflow-hidden", children: [course.thumbnail ? ((0, jsx_runtime_1.jsx)("img", { src: course.thumbnail, alt: "".concat(course.title, " thumbnail"), className: "absolute inset-0 w-full h-full object-cover", onError: function (e) { e.target.src = "/placeholder-course.jpg"; } })) : ((0, jsx_runtime_1.jsx)("div", { className: "absolute inset-0 flex items-center justify-center bg-gray-200 dark:bg-gray-700", children: (0, jsx_runtime_1.jsx)("span", { className: "text-gray-400 dark:text-gray-500 text-sm", children: "No image" }) })), (0, jsx_runtime_1.jsx)("div", { className: "absolute top-2 left-2 bg-blue-600 text-white text-xs px-2 py-1 rounded", children: levelNames[course.level] || course.level })] }), (0, jsx_runtime_1.jsx)(card_1.CardHeader, { className: "p-4 pb-0", children: (0, jsx_runtime_1.jsx)(card_1.CardTitle, { className: "text-lg", children: course.title }) }), (0, jsx_runtime_1.jsxs)(card_1.CardContent, { className: "p-4 pt-2 flex-grow", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm text-gray-500 dark:text-gray-400 line-clamp-3", children: course.description || "No description available." }), course.enrollment && ((0, jsx_runtime_1.jsxs)("div", { className: "mt-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex justify-between text-xs mb-1", children: [(0, jsx_runtime_1.jsx)("span", { children: "Progress" }), (0, jsx_runtime_1.jsxs)("span", { children: [Math.round(course.enrollment.progress), "%"] })] }), (0, jsx_runtime_1.jsx)("div", { className: "w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2", children: (0, jsx_runtime_1.jsx)("div", { className: "bg-blue-600 h-2 rounded-full", style: { width: "".concat(course.enrollment.progress, "%") } }) })] }))] }), (0, jsx_runtime_1.jsx)(card_1.CardFooter, { className: "px-4 pb-4 pt-0 mt-auto", children: (0, jsx_runtime_1.jsx)(button_1.Button, { className: "w-full", onClick: function () { return router.push("/student/course/".concat(course.id, "?teacherId=").concat(agentId)); }, children: course.enrollment ? 'Continue Learning' : 'View Course' }) })] }, course.id)); }) })] })); })(), courses.length === 0 && ((0, jsx_runtime_1.jsxs)("div", { className: "text-center py-16", children: [(0, jsx_runtime_1.jsx)("svg", { className: "mx-auto h-12 w-12 text-gray-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" }) }), (0, jsx_runtime_1.jsx)("h3", { className: "mt-2 text-sm font-medium text-gray-900 dark:text-gray-100", children: "No courses available" }), (0, jsx_runtime_1.jsx)("p", { className: "mt-1 text-sm text-gray-500 dark:text-gray-400", children: "There are no courses available from this teacher yet." })] }))] }))] })] })); }; exports.default = StudentPortalPage;