UNPKG

education-module-ai

Version:
447 lines (433 loc) 15.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.LearningPathComponent = void 0; const react_1 = __importStar(require("react")); const context_1 = require("../context"); // CSS styles as React style objects const styles = { container: { width: '100%', }, panel: { background: 'white', borderRadius: '8px', boxShadow: '0 2px 10px rgba(0, 0, 0, 0.1)', padding: '2rem', width: '100%', }, title: { margin: 0, lineHeight: 1.15, fontSize: '2rem', textAlign: 'center', }, description: { lineHeight: 1.5, fontSize: '1.2rem', textAlign: 'center', marginBottom: '2rem', }, searchContainer: { width: '100%', maxWidth: '800px', margin: '0 auto 2rem', }, formContainer: { width: '100%', maxWidth: '700px', margin: '0 auto', backgroundColor: '#f9f9f9', padding: '2rem', borderRadius: '10px', boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)', }, formGroup: { marginBottom: '1.5rem', }, formLabel: { display: 'block', marginBottom: '0.5rem', fontWeight: 'bold', }, formInput: { width: '100%', padding: '0.8rem', fontSize: '1rem', border: '1px solid #ddd', borderRadius: '5px', }, formTextarea: { width: '100%', padding: '0.8rem', fontSize: '1rem', border: '1px solid #ddd', borderRadius: '5px', minHeight: '100px', fontFamily: 'inherit', }, submitButton: { width: '100%', padding: '1rem', backgroundColor: '#0070f3', color: 'white', border: 'none', borderRadius: '5px', fontSize: '1.1rem', fontWeight: 'bold', cursor: 'pointer', transition: 'background-color 0.2s', }, submitButtonHover: { backgroundColor: '#0051a2', }, submitButtonDisabled: { backgroundColor: '#ccc', cursor: 'not-allowed', }, errorMessage: { padding: '1rem', backgroundColor: '#ffebee', borderLeft: '4px solid #d32f2f', color: '#d32f2f', margin: '1rem 0', borderRadius: '4px', }, loadingSpinner: { display: 'block', width: '40px', height: '40px', margin: '2rem auto', border: '4px solid rgba(0, 0, 0, 0.1)', borderLeftColor: '#0070f3', borderRadius: '50%', animation: 'spin 1s linear infinite', }, pathContainer: { width: '100%', maxWidth: '900px', margin: '0 auto', }, pathHeader: { marginBottom: '2rem', }, pathTitle: { fontSize: '1.8rem', fontWeight: 'bold', color: '#333', marginBottom: '0.5rem', }, pathDescription: { fontSize: '1.1rem', color: '#666', marginBottom: '1rem', }, pathMeta: { display: 'flex', flexWrap: 'wrap', gap: '1.5rem', marginBottom: '2rem', padding: '1rem', backgroundColor: '#f5f9ff', borderRadius: '8px', }, pathMetaItem: { display: 'flex', flexDirection: 'column', }, pathMetaLabel: { fontSize: '0.9rem', color: '#666', marginBottom: '0.25rem', }, pathMetaValue: { fontSize: '1.1rem', fontWeight: 'bold', color: '#333', }, prerequisitesList: { marginTop: '0.5rem', paddingLeft: '1.5rem', }, prerequisiteItem: { marginBottom: '0.5rem', }, timelineContainer: { position: 'relative', paddingTop: '1rem', }, timelineLine: { position: 'absolute', left: '1.5rem', top: 0, bottom: 0, width: '2px', backgroundColor: '#e0e0e0', zIndex: 1, }, timelineItem: { position: 'relative', marginBottom: '2.5rem', paddingLeft: '3.5rem', zIndex: 2, }, timelineNumber: { position: 'absolute', left: 0, top: '0.25rem', width: '3rem', height: '3rem', backgroundColor: '#0070f3', color: 'white', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '1.2rem', fontWeight: 'bold', }, itemContent: { backgroundColor: 'white', padding: '1.5rem', borderRadius: '8px', boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)', }, itemTitle: { fontSize: '1.4rem', marginBottom: '0.5rem', color: '#333', }, itemDescription: { fontSize: '1rem', marginBottom: '1.5rem', color: '#555', }, resourcesTitle: { fontSize: '1.1rem', fontWeight: 'bold', marginBottom: '1rem', color: '#444', }, resourcesList: { paddingLeft: 0, listStyle: 'none', }, resourceItem: { marginBottom: '1.5rem', padding: '1rem', backgroundColor: '#f9f9f9', borderRadius: '5px', borderLeft: '3px solid #0070f3', }, resourceType: { display: 'inline-block', fontSize: '0.8rem', textTransform: 'uppercase', padding: '0.2rem 0.5rem', backgroundColor: '#e3f2fd', color: '#0070f3', borderRadius: '3px', marginBottom: '0.5rem', }, resourceTitleWrapper: { marginBottom: '0.5rem', }, resourceTitle: { fontSize: '1.1rem', color: '#333', textDecoration: 'none', fontWeight: 500, marginRight: '0.5rem', }, resourceLink: { textDecoration: 'none', color: '#0070f3', fontWeight: 'bold', }, resourceDescription: { fontSize: '0.95rem', color: '#666', margin: 0, }, backButton: { marginTop: '2rem', padding: '0.8rem 1.5rem', backgroundColor: '#0070f3', color: 'white', border: 'none', borderRadius: '5px', fontSize: '1rem', cursor: 'pointer', transition: 'background-color 0.2s', display: 'block', margin: '2rem auto 0', }, }; const LearningPathComponent = ({ title = "Learning Path Generator", description = "Create a personalized learning path for any subject", onGenerate, className = "" }) => { const { apiBaseUrl } = (0, context_1.useConfig)(); const [formState, setFormState] = (0, react_1.useState)({ subject: '', currentKnowledge: '', learningGoal: '' }); const [isLoading, setIsLoading] = (0, react_1.useState)(false); const [error, setError] = (0, react_1.useState)(null); const [learningPath, setLearningPath] = (0, react_1.useState)(null); const handleInputChange = (e) => { const { name, value } = e.target; setFormState(prev => ({ ...prev, [name]: value })); }; const handleSubmit = async (e) => { e.preventDefault(); if (!formState.subject.trim() || !formState.learningGoal.trim()) return; setIsLoading(true); setError(null); setLearningPath(null); try { const response = await fetch(`${apiBaseUrl}/api/educational-contents/learning-path`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ subject: formState.subject.trim(), currentKnowledge: formState.currentKnowledge.trim() || undefined, learningGoal: formState.learningGoal.trim(), }), }); if (!response.ok) { const errorData = await response.json().catch(() => null); throw new Error(errorData && errorData.error ? errorData.error : `Server responded with ${response.status}: ${response.statusText}`); } const data = await response.json(); setLearningPath(data.data); if (onGenerate) { onGenerate(data.data); } } catch (err) { setError(err instanceof Error ? err.message : 'Failed to generate learning path'); console.error('Error generating learning path:', err); } finally { setIsLoading(false); } }; const resetForm = () => { setLearningPath(null); setError(null); }; return (<div style={{ ...styles.container, ...(className ? { className } : {}) }}> <div style={styles.panel}> <h1 style={styles.title}>{title}</h1> <p style={styles.description}> {description} </p> {!learningPath ? (<div style={styles.formContainer}> <form onSubmit={handleSubmit}> <div style={styles.formGroup}> <label style={styles.formLabel} htmlFor="subject">Subject/Topic</label> <input type="text" id="subject" name="subject" value={formState.subject} onChange={handleInputChange} placeholder="e.g., Machine Learning, World History, Piano" style={styles.formInput} required/> </div> <div style={styles.formGroup}> <label style={styles.formLabel} htmlFor="currentKnowledge">Your Current Knowledge (Optional)</label> <textarea id="currentKnowledge" name="currentKnowledge" value={formState.currentKnowledge} onChange={handleInputChange} placeholder="Describe what you already know about this subject" style={styles.formTextarea}/> </div> <div style={styles.formGroup}> <label style={styles.formLabel} htmlFor="learningGoal">Your Learning Goal</label> <textarea id="learningGoal" name="learningGoal" value={formState.learningGoal} onChange={handleInputChange} placeholder="What do you want to achieve? Be as specific as possible." style={styles.formTextarea} required/> </div> <button type="submit" style={{ ...styles.submitButton, ...(isLoading || !formState.subject.trim() || !formState.learningGoal.trim() ? styles.submitButtonDisabled : {}) }} disabled={isLoading || !formState.subject.trim() || !formState.learningGoal.trim()}> {isLoading ? 'Generating Path...' : 'Create Learning Path'} </button> </form> {error && (<div style={styles.errorMessage}> <p>{error}</p> <p>Make sure your backend server is running at http://localhost:3009</p> </div>)} </div>) : (<div style={styles.pathContainer}> <div style={styles.pathHeader}> <h2 style={styles.pathTitle}>{learningPath.subject}</h2> <p style={styles.pathDescription}>{learningPath.overview}</p> <div style={styles.pathMeta}> <div style={styles.pathMetaItem}> <span style={styles.pathMetaLabel}>Estimated Time to Complete</span> <span style={styles.pathMetaValue}>{learningPath.estimatedCompletionTime}</span> </div> <div style={styles.pathMetaItem}> <span style={styles.pathMetaLabel}>Prerequisites</span> {learningPath.prerequisites.length > 0 ? (<ul style={styles.prerequisitesList}> {learningPath.prerequisites.map((prereq, index) => (<li key={index} style={styles.prerequisiteItem}>{prereq}</li>))} </ul>) : (<span style={styles.pathMetaValue}>None</span>)} </div> </div> </div> <div style={styles.timelineContainer}> <div style={styles.timelineLine}></div> {learningPath.steps.map((step, index) => (<div key={index} style={styles.timelineItem}> <div style={styles.timelineNumber}>{index + 1}</div> <div style={styles.itemContent}> <h3 style={styles.itemTitle}>{step.title}</h3> <p style={styles.itemDescription}>{step.description}</p> <h4 style={styles.resourcesTitle}>Recommended Resources</h4> <ul style={styles.resourcesList}> {step.resources.map((resource, resIndex) => (<li key={resIndex} style={styles.resourceItem}> <span style={styles.resourceType}>{resource.type}</span> <div style={styles.resourceTitleWrapper}> {resource.url ? (<a href={resource.url} target="_blank" rel="noopener noreferrer" style={styles.resourceLink}> {resource.title} </a>) : (<span style={styles.resourceTitle}>{resource.title}</span>)} </div> <p style={styles.resourceDescription}>{resource.description}</p> </li>))} </ul> </div> </div>))} </div> <button onClick={resetForm} style={styles.backButton}> Create Another Learning Path </button> </div>)} {isLoading && (<div style={styles.loadingSpinner}></div>)} </div> </div>); }; exports.LearningPathComponent = LearningPathComponent;