education-module-ai
Version:
AI Education Module using Agent Framework
388 lines (376 loc) • 13.7 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;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnswerQuestionComponent = void 0;
const react_1 = __importStar(require("react"));
const react_markdown_1 = __importDefault(require("react-markdown"));
const context_1 = require("../context");
const subjects = [
{ value: 'mathematics', label: 'Mathematics' },
{ value: 'science', label: 'Science' },
{ value: 'history', label: 'History' },
{ value: 'language_arts', label: 'Language Arts' },
{ value: 'computer_science', label: 'Computer Science' },
{ value: 'art', label: 'Art' },
{ value: 'music', label: 'Music' }
];
const levels = [
{ value: 'elementary', label: 'Elementary' },
{ value: 'middle_school', label: 'Middle School' },
{ value: 'high_school', label: 'High School' },
{ value: 'undergraduate', label: 'Undergraduate' },
{ value: 'graduate', label: 'Graduate' },
{ value: 'professional', label: 'Professional' }
];
// 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',
},
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',
},
textInput: {
width: '100%',
padding: '0.8rem',
fontSize: '1rem',
border: '1px solid #ddd',
borderRadius: '5px',
},
textarea: {
width: '100%',
padding: '0.8rem',
fontSize: '1rem',
border: '1px solid #ddd',
borderRadius: '5px',
minHeight: '150px',
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',
},
responseSection: {
marginTop: '2rem',
backgroundColor: 'white',
padding: '2rem',
borderRadius: '10px',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
},
responseTitle: {
marginBottom: '1rem',
fontSize: '1.5rem',
color: '#333',
},
responseContent: {
backgroundColor: '#f9f9f9',
padding: '1.5rem',
borderRadius: '5px',
borderLeft: '4px solid #0070f3',
lineHeight: 1.6,
whiteSpace: 'pre-wrap',
},
goBackButton: {
marginTop: '1.5rem',
padding: '0.8rem 1.5rem',
backgroundColor: '#0070f3',
color: 'white',
border: 'none',
borderRadius: '5px',
fontSize: '1rem',
cursor: 'pointer',
transition: 'background-color 0.2s',
},
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',
},
errorMessage: {
padding: '1rem',
backgroundColor: '#ffebee',
borderLeft: '4px solid #d32f2f',
color: '#d32f2f',
margin: '1rem 0',
borderRadius: '4px',
},
questionHistory: {
marginTop: '2rem',
paddingTop: '1.5rem',
borderTop: '1px solid #eaeaea',
},
questionHistoryTitle: {
marginBottom: '1rem',
fontSize: '1.2rem',
},
questionHistoryList: {
paddingLeft: 0,
listStyle: 'none',
},
questionHistoryItem: {
padding: '0.8rem',
marginBottom: '0.5rem',
backgroundColor: '#f0f7ff',
borderRadius: '5px',
cursor: 'pointer',
transition: 'background-color 0.2s',
},
questionHistoryItemHover: {
backgroundColor: '#e3f2fd',
},
relatedConcepts: {
marginTop: '2rem',
},
relatedConceptsTitle: {
marginBottom: '1rem',
fontSize: '1.2rem',
},
conceptTags: {
display: 'flex',
flexWrap: 'wrap',
gap: '0.5rem',
},
conceptTag: {
backgroundColor: '#e3f2fd',
color: '#0070f3',
padding: '0.4rem 0.8rem',
borderRadius: '16px',
fontSize: '0.9rem',
},
furtherReading: {
marginTop: '2rem',
},
furtherReadingTitle: {
marginBottom: '1rem',
fontSize: '1.2rem',
},
furtherReadingList: {
paddingLeft: '1.5rem',
margin: 0,
},
furtherReadingItem: {
marginBottom: '0.5rem',
},
};
const AnswerQuestionComponent = ({ title = "Ask Educational Questions", description = "Get answers to your educational questions from our AI tutor", onAnswer, className = "" }) => {
const { apiBaseUrl } = (0, context_1.useConfig)();
const [formState, setFormState] = (0, react_1.useState)({
subject: 'computer_science',
level: 'intermediate',
question: ''
});
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
const [error, setError] = (0, react_1.useState)(null);
const [response, setResponse] = (0, react_1.useState)(null);
const [questionHistory, setQuestionHistory] = (0, react_1.useState)([]);
const handleInputChange = (e) => {
const { name, value } = e.target;
setFormState(prev => ({ ...prev, [name]: value }));
};
const handleSubmit = async (e) => {
e.preventDefault();
if (!formState.question.trim())
return;
setIsLoading(true);
setError(null);
try {
const response = await fetch(`${apiBaseUrl}/api/educational-contents/answer-question`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
subject: formState.subject,
level: formState.level,
question: formState.question.trim()
}),
});
if (!response.ok) {
throw new Error(`Server responded with ${response.status}: ${response.statusText}`);
}
const data = await response.json();
setResponse(data.data);
// Add to history
setQuestionHistory(prev => [
{ question: formState.question, answer: data.data },
...prev.slice(0, 4) // Keep only the 5 most recent questions
]);
if (onAnswer) {
onAnswer(formState.question, data.data);
}
// Clear question field
setFormState(prev => ({ ...prev, question: '' }));
}
catch (err) {
setError(err instanceof Error ? err.message : 'Failed to get answer');
console.error('Error getting answer:', err);
}
finally {
setIsLoading(false);
}
};
const handleHistoryItemClick = (item) => {
setFormState(prev => ({ ...prev, question: item.question }));
setResponse(item.answer);
};
return (<div style={{ ...styles.container, ...(className ? { className } : {}) }}>
<div style={styles.panel}>
<h1 style={styles.title}>{title}</h1>
<p style={styles.description}>
{description}
</p>
{!response ? (<div style={styles.formContainer}>
<form onSubmit={handleSubmit}>
<div style={styles.formGroup}>
<label style={styles.formLabel} htmlFor="subject">Subject</label>
<select id="subject" name="subject" value={formState.subject} onChange={handleInputChange} style={styles.textInput}>
{subjects.map(subject => (<option key={subject.value} value={subject.value}>
{subject.label}
</option>))}
</select>
</div>
<div style={styles.formGroup}>
<label style={styles.formLabel} htmlFor="level">Educational Level</label>
<select id="level" name="level" value={formState.level} onChange={handleInputChange} style={styles.textInput}>
{levels.map(level => (<option key={level.value} value={level.value}>
{level.label}
</option>))}
</select>
</div>
<div style={styles.formGroup}>
<label style={styles.formLabel} htmlFor="question">Your Question</label>
<textarea id="question" name="question" value={formState.question} onChange={handleInputChange} placeholder="e.g., How do photosynthesis works? What is the theory of relativity?" style={styles.textarea} rows={5} required/>
</div>
<button type="submit" style={{
...styles.submitButton,
...(isLoading || !formState.question.trim() ? styles.submitButtonDisabled : {})
}} disabled={isLoading || !formState.question.trim()}>
{isLoading ? 'Getting Answer...' : 'Ask Question'}
</button>
</form>
{error && (<div style={styles.errorMessage}>
<p>{error}</p>
<p>Make sure your backend server is running at {apiBaseUrl}</p>
</div>)}
{questionHistory.length > 0 && (<div style={styles.questionHistory}>
<h3 style={styles.questionHistoryTitle}>Recent Questions</h3>
<ul style={styles.questionHistoryList}>
{questionHistory.map((item, index) => (<li key={index} onClick={() => handleHistoryItemClick(item)} style={styles.questionHistoryItem}>
{item.question}
</li>))}
</ul>
</div>)}
</div>) : (<div style={styles.responseSection}>
<h2 style={styles.responseTitle}>Answer</h2>
<div style={styles.responseContent}>
<react_markdown_1.default>{response.answer}</react_markdown_1.default>
</div>
{response.relatedConcepts && response.relatedConcepts.length > 0 && (<div style={styles.relatedConcepts}>
<h3 style={styles.relatedConceptsTitle}>Related Concepts</h3>
<div style={styles.conceptTags}>
{response.relatedConcepts.map((concept, index) => (<span key={index} style={styles.conceptTag}>{concept}</span>))}
</div>
</div>)}
{response.furtherReadings && response.furtherReadings.length > 0 && (<div style={styles.furtherReading}>
<h3 style={styles.furtherReadingTitle}>Further Reading</h3>
<ul style={styles.furtherReadingList}>
{response.furtherReadings.map((reading, index) => (<li key={index} style={styles.furtherReadingItem}>{reading}</li>))}
</ul>
</div>)}
<button onClick={() => setResponse(null)} style={styles.goBackButton}>
Ask Another Question
</button>
</div>)}
{isLoading && (<div style={styles.loadingSpinner}></div>)}
</div>
</div>);
};
exports.AnswerQuestionComponent = AnswerQuestionComponent;