education-module-ai
Version:
AI Education Module using Agent Framework
447 lines (433 loc) • 15.1 kB
JSX
;
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;