UNPKG

education-module-ai

Version:
388 lines (376 loc) 13.7 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; }; })(); 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;